diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift index e62c808f2fc..e40ffa2a39f 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift @@ -66,6 +66,7 @@ protocol PointOfSaleAggregateModelProtocol { var orderState: PointOfSaleOrderState { orderController.orderState.externalState } private var internalOrderState: PointOfSaleInternalOrderState { orderController.orderState } + let entryPointController: POSEntryPointController let purchasableItemsController: PointOfSaleItemsControllerProtocol let purchasableItemsSearchController: PointOfSaleSearchingItemsControllerProtocol let popularPurchasableItemsController: PointOfSaleItemsControllerProtocol @@ -99,7 +100,8 @@ protocol PointOfSaleAggregateModelProtocol { _viewStateCoordinator } - init(itemsController: PointOfSaleItemsControllerProtocol, + init(entryPointController: POSEntryPointController, + itemsController: PointOfSaleItemsControllerProtocol, purchasableItemsSearchController: PointOfSaleSearchingItemsControllerProtocol, couponsController: PointOfSaleCouponsControllerProtocol, couponsSearchController: PointOfSaleSearchingItemsControllerProtocol, @@ -112,6 +114,7 @@ protocol PointOfSaleAggregateModelProtocol { barcodeScanService: PointOfSaleBarcodeScanServiceProtocol, soundPlayer: PointOfSaleSoundPlayerProtocol = PointOfSaleSoundPlayer(), paymentState: PointOfSalePaymentState = .idle) { + self.entryPointController = entryPointController self.purchasableItemsController = itemsController self.purchasableItemsSearchController = purchasableItemsSearchController self.couponsController = couponsController diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift index a84a2e58843..8d253b47af3 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift @@ -32,35 +32,56 @@ struct PointOfSaleDashboardView: View { } } + // MARK: View State + + enum ViewState: Equatable { + case loading + case ineligible(reason: POSIneligibleReason) + case error(PointOfSaleErrorState) + case content + case unsupportedWidth + } + + private var viewState: ViewState { + PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: posModel.entryPointController.eligibilityState, + itemsContainerState: itemsViewState.containerState, + horizontalSizeClass: horizontalSizeClass + ) + } + var body: some View { @Bindable var posModel = posModel ZStack(alignment: .bottomLeading) { - if case .regular = horizontalSizeClass { - switch itemsViewState.containerState { - case .loading: - PointOfSaleLoadingView() - .transition(.opacity) - .ignoresSafeArea() - case .error(let error): - PointOfSaleItemListFullscreenErrorView(error: error, onAction: { - Task { - switch viewStateCoordinator.selectedItemListType { - case .products(search: false): - await posModel.purchasableItemsController.loadItems(base: .root) - case .products(search: true): - await posModel.purchasableItemsSearchController.loadItems(base: .root) - case .coupons(search: false): - await posModel.couponsSearchController.loadItems(base: .root) - case .coupons(search: true): - await posModel.couponsSearchController.loadItems(base: .root) - } + switch viewState { + case .loading: + PointOfSaleLoadingView() + .transition(.opacity) + .ignoresSafeArea() + case .ineligible(let reason): + POSIneligibleView(reason: reason, onRefresh: { + try await posModel.entryPointController.refreshEligibility(reason: reason) + }) + .frame(maxWidth: .infinity) + case .error(let error): + PointOfSaleItemListFullscreenErrorView(error: error, onAction: { + Task { + switch viewStateCoordinator.selectedItemListType { + case .products(search: false): + await posModel.purchasableItemsController.loadItems(base: .root) + case .products(search: true): + await posModel.purchasableItemsSearchController.loadItems(base: .root) + case .coupons(search: false): + await posModel.couponsSearchController.loadItems(base: .root) + case .coupons(search: true): + await posModel.couponsSearchController.loadItems(base: .root) } - }) - case .content: - contentView - .accessibilitySortPriority(2) - } - } else { + } + }) + case .content: + contentView + .accessibilitySortPriority(2) + case .unsupportedWidth: PointOfSaleUnsupportedWidthView() .transition(.opacity) .ignoresSafeArea() @@ -73,7 +94,7 @@ struct PointOfSaleDashboardView: View { .padding(.bottom, Constants.floatingControlBottomPadding) .trackSize(size: $floatingSize) .accessibilitySortPriority(1) - .renderedIf(itemsViewState.containerState != .loading) + .renderedIf(viewState.showsFloatingControl) POSConnectivityView() } @@ -81,7 +102,7 @@ struct PointOfSaleDashboardView: View { CGSizeMake(floatingSize.width + Constants.floatingControlHorizontalOffset, floatingSize.height + Constants.floatingControlVerticalOffset)) .environment(\.posBackgroundAppearance, backgroundAppearance) - .animation(.easeInOut, value: itemsViewState.containerState == .loading) + .animation(.easeInOut, value: viewState == .loading) .background(Color.posSurface) .navigationBarBackButtonHidden(true) .posModal(item: $posModel.cardPresentPaymentOnboardingViewModel, onDismiss: { @@ -108,10 +129,13 @@ struct PointOfSaleDashboardView: View { .sheet(isPresented: $showDocumentation) { documentationView } - .task { - await posModel.purchasableItemsController.loadItems(base: .root) - await posModel.couponsController.loadItems(base: .root) - await posModel.popularPurchasableItemsController.loadItems(base: .root) + .onChange(of: posModel.entryPointController.eligibilityState) { oldValue, newValue in + guard newValue == .eligible else { return } + Task { @MainActor in + await posModel.purchasableItemsController.loadItems(base: .root) + await posModel.couponsController.loadItems(base: .root) + await posModel.popularPurchasableItemsController.loadItems(base: .root) + } } .ignoresSafeArea(.keyboard) } diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift index cbc09059d72..e4534694753 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift @@ -50,20 +50,11 @@ struct PointOfSaleEntryPointView: View { var body: some View { Group { - switch posEntryPointController.eligibilityState { - case .none: + if let posModel { + PointOfSaleDashboardView() + .environment(posModel) + } else { PointOfSaleLoadingView() - case .eligible: - if let posModel = posModel { - PointOfSaleDashboardView() - .environment(posModel) - } else { - PointOfSaleLoadingView() - } - case let .ineligible(reason): - POSIneligibleView(reason: reason, onRefresh: { - try await posEntryPointController.refreshEligibility(reason: reason) - }) } } .task { @@ -71,6 +62,7 @@ struct PointOfSaleEntryPointView: View { // Confusingly, init can be called more than once, but `task` matches the lifecycle. // See https://developer.apple.com/documentation/swiftui/state#Store-observable-objects for details. posModel = PointOfSaleAggregateModel( + entryPointController: posEntryPointController, itemsController: itemsController, purchasableItemsSearchController: purchasableItemsSearchController, couponsController: couponsController, diff --git a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift index 5fc3f7a53c9..89706617730 100644 --- a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift +++ b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift @@ -219,6 +219,7 @@ struct POSPreviewHelpers { barcodeScanService: PointOfSaleBarcodeScanServiceProtocol = PointOfSalePreviewBarcodeScanService() ) -> PointOfSaleAggregateModel { return PointOfSaleAggregateModel( + entryPointController: POSEntryPointController(eligibilityChecker: LegacyPOSTabEligibilityChecker(siteID: 0)), itemsController: itemsController, purchasableItemsSearchController: purchasableItemsSearchController, couponsController: couponsController, diff --git a/WooCommerce/Classes/POS/ViewHelpers/PointOfSaleDashboardViewHelper.swift b/WooCommerce/Classes/POS/ViewHelpers/PointOfSaleDashboardViewHelper.swift new file mode 100644 index 00000000000..0797b314d52 --- /dev/null +++ b/WooCommerce/Classes/POS/ViewHelpers/PointOfSaleDashboardViewHelper.swift @@ -0,0 +1,45 @@ +import Foundation +import SwiftUI + +@available(iOS 17.0, *) +struct PointOfSaleDashboardViewHelper { + static func determineViewState( + eligibilityState: POSEligibilityState?, + itemsContainerState: ItemsContainerState, + horizontalSizeClass: UserInterfaceSizeClass? + ) -> PointOfSaleDashboardView.ViewState { + guard case .regular = horizontalSizeClass else { + return .unsupportedWidth + } + + guard let eligibilityState else { + return .loading + } + + switch eligibilityState { + case .eligible: + switch itemsContainerState { + case .loading: + return .loading + case .error(let error): + return .error(error) + case .content: + return .content + } + case .ineligible(let reason): + return .ineligible(reason: reason) + } + } +} + +@available(iOS 17.0, *) +extension PointOfSaleDashboardView.ViewState { + var showsFloatingControl: Bool { + switch self { + case .content, .error, .unsupportedWidth: + return true + case .loading, .ineligible: + return false + } + } +} diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 227e359a65c..a1ac954977f 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -359,6 +359,8 @@ 026826C22BF59E410036F959 /* PointOfSaleCardPresentPaymentRequiredReaderUpdateInProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026826B92BF59E400036F959 /* PointOfSaleCardPresentPaymentRequiredReaderUpdateInProgressView.swift */; }; 026826C42BF59E410036F959 /* PointOfSaleCardPresentPaymentFoundReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026826BB2BF59E410036F959 /* PointOfSaleCardPresentPaymentFoundReaderView.swift */; }; 026826C72BF59E410036F959 /* PointOfSaleCardPresentPaymentScanningForReadersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026826BE2BF59E410036F959 /* PointOfSaleCardPresentPaymentScanningForReadersView.swift */; }; + 026878D62E293E7C00DBFD34 /* PointOfSaleDashboardViewHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026878D52E293E7300DBFD34 /* PointOfSaleDashboardViewHelper.swift */; }; + 026878D82E2942E400DBFD34 /* PointOfSaleDashboardViewHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026878D72E2942E200DBFD34 /* PointOfSaleDashboardViewHelperTests.swift */; }; 02691780232600A6002AFC20 /* ProductsTabProductViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0269177F232600A6002AFC20 /* ProductsTabProductViewModelTests.swift */; }; 02691782232605B9002AFC20 /* PaginatedListViewControllerStateCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02691781232605B9002AFC20 /* PaginatedListViewControllerStateCoordinatorTests.swift */; }; 0269576A23726304001BA0BF /* KeyboardFrameObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0269576923726304001BA0BF /* KeyboardFrameObserver.swift */; }; @@ -3528,6 +3530,8 @@ 026826B92BF59E400036F959 /* PointOfSaleCardPresentPaymentRequiredReaderUpdateInProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentRequiredReaderUpdateInProgressView.swift; sourceTree = ""; }; 026826BB2BF59E410036F959 /* PointOfSaleCardPresentPaymentFoundReaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentFoundReaderView.swift; sourceTree = ""; }; 026826BE2BF59E410036F959 /* PointOfSaleCardPresentPaymentScanningForReadersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentScanningForReadersView.swift; sourceTree = ""; }; + 026878D52E293E7300DBFD34 /* PointOfSaleDashboardViewHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleDashboardViewHelper.swift; sourceTree = ""; }; + 026878D72E2942E200DBFD34 /* PointOfSaleDashboardViewHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleDashboardViewHelperTests.swift; sourceTree = ""; }; 0269177F232600A6002AFC20 /* ProductsTabProductViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsTabProductViewModelTests.swift; sourceTree = ""; }; 02691781232605B9002AFC20 /* PaginatedListViewControllerStateCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginatedListViewControllerStateCoordinatorTests.swift; sourceTree = ""; }; 0269576923726304001BA0BF /* KeyboardFrameObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardFrameObserver.swift; sourceTree = ""; }; @@ -7085,6 +7089,7 @@ 026826912BF59D7A0036F959 /* ViewHelpers */ = { isa = PBXGroup; children = ( + 026878D52E293E7300DBFD34 /* PointOfSaleDashboardViewHelper.swift */, 6891C3632D364AFB00B5B48C /* CollectCashViewHelper.swift */, 6885E2CB2C32B14B004C8D70 /* TotalsViewHelper.swift */, 6837631B2C2E847D00AD51D0 /* CartViewHelper.swift */, @@ -13016,6 +13021,7 @@ children = ( 683763192C2E6F5900AD51D0 /* CartViewHelperTests.swift */, 6891C3652D364C1A00B5B48C /* CollectCashViewHelperTests.swift */, + 026878D72E2942E200DBFD34 /* PointOfSaleDashboardViewHelperTests.swift */, 208628732D48E4CB003F45DC /* TotalsViewHelperTests.swift */, ); path = ViewHelpers; @@ -16278,6 +16284,7 @@ 68E674AD2A4DAC010034BA1E /* CurrentPlanDetailsView.swift in Sources */, 20134CE82D4D38E000076A80 /* CardPresentPaymentPlugin+SetUpTapToPay.swift in Sources */, DE68B81F26F86B1700C86CFB /* OfflineBannerView.swift in Sources */, + 026878D62E293E7C00DBFD34 /* PointOfSaleDashboardViewHelper.swift in Sources */, B90D21802D1ED1F300ED60ED /* WooShippingCustomsItemOriginCountryInfoDialog.swift in Sources */, D8610BCC256F284700A5DF27 /* ULErrorViewModel.swift in Sources */, CCFC50552743BC0D001E505F /* OrderForm.swift in Sources */, @@ -16930,6 +16937,7 @@ 019130212CF5B0FF008C0C88 /* TapToPayEducationViewModelTests.swift in Sources */, 026A50302D2F80B5002C42C2 /* ThresholdInfiniteScrollTriggerDeterminerTests.swift in Sources */, CC33238C29CDF67D00CA9709 /* ComponentSettingsViewModelTests.swift in Sources */, + 026878D82E2942E400DBFD34 /* PointOfSaleDashboardViewHelperTests.swift in Sources */, 86F5FFE42CA30D9200C767C4 /* CustomFieldsListViewModelTests.swift in Sources */, 01AB2D162DDC8CDA00AA67FD /* MockAnalytics.swift in Sources */, 0261F5A728D454CF00B7AC72 /* ProductSearchUICommandTests.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift index 6e70077e471..fc3dffbcda1 100644 --- a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift @@ -1,6 +1,8 @@ import Testing import Foundation @testable import WooCommerce +import protocol WooFoundation.Analytics +import protocol Yosemite.PointOfSaleBarcodeScanServiceProtocol import protocol Yosemite.POSOrderableItem import enum Yosemite.POSItem @testable import struct Yosemite.POSSimpleProduct @@ -14,18 +16,7 @@ struct PointOfSaleAggregateModelTests { @available(iOS 17.0, *) @Test func inits_with_building_order_stage() async throws { // Given - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel() // Then #expect(sut.orderStage == .building) } @@ -33,18 +24,7 @@ struct PointOfSaleAggregateModelTests { @available(iOS 17.0, *) @Test func startNewCart_removes_all_items_from_cart_and_moves_back_to_building() async throws { // Given - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel() sut.addToCart(makePurchasableItem()) await sut.checkOut() try #require(sut.orderStage == .finalizing) @@ -61,18 +41,7 @@ struct PointOfSaleAggregateModelTests { @available(iOS 17.0, *) @Test func checkOut_moves_to_finalizing_order_stage() async throws { // Given - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel() sut.addToCart(makePurchasableItem()) // When @@ -85,18 +54,7 @@ struct PointOfSaleAggregateModelTests { @available(iOS 17.0, *) @Test func addMoreToCart_moves_to_building_order_stage() async throws { // Given - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel() sut.addToCart(makePurchasableItem()) await sut.checkOut() try #require(sut.orderStage == .finalizing) @@ -158,18 +116,7 @@ struct PointOfSaleAggregateModelTests { @available(iOS 17.0, *) @Test func addItem_results_in_a_non_empty_cart() async throws { // Given - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: analytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel(analytics: analytics) try #require(sut.cart.isEmpty) let item = makePurchasableItem() @@ -183,18 +130,7 @@ struct PointOfSaleAggregateModelTests { @available(iOS 17.0, *) @Test func addItem_puts_new_items_first_in_the_cart() async throws { // Given - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: analytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel(analytics: analytics) let items = [makePurchasableItem(), makePurchasableItem(), makePurchasableItem()] // When @@ -213,18 +149,7 @@ struct PointOfSaleAggregateModelTests { @available(iOS 17.0, *) @Test func removeItem_after_adding_two_items_removes_item_correctly() async throws { // Given - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: analytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel(analytics: analytics) let item = makePurchasableItem(name: "Item 1") let anotherItem = makePurchasableItem(name: "Item 2") @@ -244,18 +169,7 @@ struct PointOfSaleAggregateModelTests { @available(iOS 17.0, *) @Test func removeAllItemsFromCart_removes_everything() async throws { // Given - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: analytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel(analytics: analytics) let item = makePurchasableItem(name: "Item 1") let anotherItem = makePurchasableItem(name: "Item 2") @@ -273,18 +187,7 @@ struct PointOfSaleAggregateModelTests { @available(iOS 17.0, *) @Test func removeAllItemsFromCartOfCouponType_removes_coupons() async throws { // Given - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: analytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel(analytics: analytics) let item = makePurchasableItem(name: "Item 1") let anotherItem = makePurchasableItem(name: "Item 2") let couponItem = makeCouponItem(code: "VALID") @@ -319,19 +222,11 @@ struct PointOfSaleAggregateModelTests { @Test func startNewCart_calls_clearOrder() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + ) sut.addToCart(makePurchasableItem()) @@ -346,19 +241,11 @@ struct PointOfSaleAggregateModelTests { @Test func checkout_with_items_calls_sync_order() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + ) sut.addToCart(makePurchasableItem()) sut.addToCart(makePurchasableItem()) @@ -376,19 +263,11 @@ struct PointOfSaleAggregateModelTests { @Test func checkOut_without_items_calls_sync_order() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + ) sut.addToCart(makePurchasableItem()) sut.removeAllItemsFromCart() @@ -405,19 +284,10 @@ struct PointOfSaleAggregateModelTests { @Test func when_collectPayment_is_called_channel_is_set_to_pos() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) sut.addToCart(makePurchasableItem()) cardPresentPaymentService.connectedReader = .init(name: "Test reader", batteryLevel: 0.7) @@ -435,18 +305,7 @@ struct PointOfSaleAggregateModelTests { @Test func sendReceipt_when_invoked_then_calls_controller() async throws { // Given let orderController = MockPointOfSaleOrderController() - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel(orderController: orderController) // When try await sut.sendReceipt(to: "") @@ -462,18 +321,7 @@ struct PointOfSaleAggregateModelTests { orderController.shouldThrowReceiptError = true let expectedError = NSError(domain: "some error", code: -1) - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel(orderController: orderController) do { // When @@ -490,19 +338,10 @@ struct PointOfSaleAggregateModelTests { @Test func when_pointOfSaleClosed_then_order_is_cleared_up() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) sut.addToCart(makePurchasableItem()) @@ -524,19 +363,10 @@ struct PointOfSaleAggregateModelTests { // Given that we don't specify a payment state // When we init let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) // Then #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .idle)) @@ -574,18 +404,10 @@ struct PointOfSaleAggregateModelTests { @Test func startNewCart_sets_card_payment_state_to_idle() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer(), paymentState: PointOfSalePaymentState(card: .cardPaymentSuccessful, cash: .idle)) // When @@ -599,19 +421,10 @@ struct PointOfSaleAggregateModelTests { @Test func startNewCart_sets_payment_message_to_nil() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) cardPresentPaymentService.paymentEvent = .show(eventDetails: .paymentSuccess(done: {})) try #require(sut.cardPresentPaymentInlineMessage != nil) @@ -627,18 +440,10 @@ struct PointOfSaleAggregateModelTests { @Test func addMoreToCart_sets_card_payment_state_to_idle() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer(), paymentState: PointOfSalePaymentState(card: .cardPaymentSuccessful, cash: .idle)) // When @@ -652,19 +457,10 @@ struct PointOfSaleAggregateModelTests { @Test func addMoreToCart_sets_payment_message_to_nil() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) cardPresentPaymentService.paymentEvent = .show( eventDetails: .tapSwipeOrInsertCard( @@ -684,19 +480,10 @@ struct PointOfSaleAggregateModelTests { @Test func startCashPayment_calls_for_ongoing_card_payment_cancellation() async { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) // When await sut.startCashPayment() @@ -710,19 +497,10 @@ struct PointOfSaleAggregateModelTests { @Test func startCashPayment_sets_payment_state_to_collectingCash() async { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) // When await sut.startCashPayment() @@ -735,19 +513,10 @@ struct PointOfSaleAggregateModelTests { @Test func cancelCashPayment_resets_payment_state_to_idle() async { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) await sut.startCashPayment() #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .collectingCash)) @@ -762,19 +531,10 @@ struct PointOfSaleAggregateModelTests { @Test func cancelCashPayment_maintains_order_stage_as_finalizing() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) #expect(sut.orderStage == .building) await sut.checkOut() @@ -799,19 +559,10 @@ struct PointOfSaleAggregateModelTests { orderTotalDecimal: 52.3, order: Order.fake().copy(currency: "$", total: "52.30")) let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) // When cardPresentPaymentService.paymentEvent = .show(eventDetails: .paymentSuccess(done: {})) @@ -828,19 +579,10 @@ struct PointOfSaleAggregateModelTests { @Test func paymentIntentCreationErrorMessage_when_paymentIntentCreationError_tryAgain_cancels_payment() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) struct TestError: Error {} // When paymentIntentCreationError event is received @@ -863,19 +605,10 @@ struct PointOfSaleAggregateModelTests { @Test func paymentIntentCreationErrorMessage_when_paymentIntentCreationError_editOrder_moves_back_to_building() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) struct TestError: Error {} await sut.checkOut() @@ -901,19 +634,10 @@ struct PointOfSaleAggregateModelTests { @Test func checkOut_when_reader_connects_collectPayment_called() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) cardPresentPaymentService.connectedReader = nil orderController.orderStateToReturn = makeLoadedOrderState(orderTotal: "$1.00", orderTotalDecimal: 1) @@ -938,19 +662,10 @@ struct PointOfSaleAggregateModelTests { @Test func checkOut_when_reader_is_already_connected_and_order_more_than_zero_collectPayment_called() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) cardPresentPaymentService.connectedReader = .init(name: "Test reader", batteryLevel: 0.7) orderController.orderStateToReturn = makeLoadedOrderState(orderTotal: "$0.01", orderTotalDecimal: 0.01) @@ -965,19 +680,10 @@ struct PointOfSaleAggregateModelTests { @Test func checkOut_when_reader_is_already_connected_and_order_is_free_collectPayment_is_not_called() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) cardPresentPaymentService.connectedReader = .init(name: "Test reader", batteryLevel: 0.7) orderController.orderStateToReturn = makeLoadedOrderState(orderTotal: "$0.00", orderTotalDecimal: 0.0) @@ -992,19 +698,10 @@ struct PointOfSaleAggregateModelTests { @Test func after_disconnection_when_reader_reconnects_collectPayment_called() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) cardPresentPaymentService.connectedReader = CardPresentPaymentCardReader(name: "Test", batteryLevel: 0.5) orderController.orderStateToReturn = makeLoadedOrderState(orderTotal: "$1.00", orderTotalDecimal: 1) @@ -1030,19 +727,10 @@ struct PointOfSaleAggregateModelTests { @Test(.disabled()) func cancelThenCollectPayment_still_collects_payment_when_cancellation_fails() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) orderController.orderStateToReturn = makeLoadedOrderState(cartTotal: "$1.00") await orderController.syncOrder(for: .init(), retryHandler: {}) @@ -1063,19 +751,10 @@ struct PointOfSaleAggregateModelTests { @Test func cardPresentPaymentOnboardingViewModel_is_non_nil_when_onboarding_is_required() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) let onboardingViewModel = CardPresentPaymentsOnboardingViewModel( fixedState: .pluginNotActivated(plugin: .stripe), useCase: MockCardPresentPaymentsOnboardingUseCase(initial: .pluginNotActivated(plugin: .stripe)) @@ -1094,19 +773,10 @@ struct PointOfSaleAggregateModelTests { @Test func connectionSuccessAlert_is_filtered_when_waiting_to_start_payment_on_card_reader_connection() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) // Add item to cart and checkout to trigger payment waiting state sut.addToCart(makePurchasableItem()) @@ -1129,19 +799,10 @@ struct PointOfSaleAggregateModelTests { @Test func connectionSuccessAlert_is_shown_when_not_waiting_to_start_payment() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + orderController: orderController) // Verify we're in building stage and no alert is currently shown try #require(sut.orderStage == .building) @@ -1175,19 +836,11 @@ struct PointOfSaleAggregateModelTests { @Test func paymentsOnboardingDismissed_event_is_tracked_with_state_when_cancelOnboarding_is_invoked() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, - analytics: analytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + analytics: analytics) sut.addToCart(makePurchasableItem()) @@ -1211,19 +864,11 @@ struct PointOfSaleAggregateModelTests { @Test func pointOfSalePaymentsOnboardingShown_event_is_tracked_when_trackOnboardingShown_is_invoked() async throws { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, - analytics: analytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + analytics: analytics) sut.addToCart(makePurchasableItem()) @@ -1238,19 +883,11 @@ struct PointOfSaleAggregateModelTests { @Test func connectCardReader_when_tapped_then_tracks_event() { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, - analytics: analytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + analytics: analytics) //When sut.connectCardReader() @@ -1263,19 +900,11 @@ struct PointOfSaleAggregateModelTests { @Test func disconnectCardReader_when_tapped_then_tracks_event() { // Given let itemsController = MockPointOfSaleItemsController() - let sut = PointOfSaleAggregateModel( + let sut = makePointOfSaleAggregateModel( itemsController: itemsController, - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, - analytics: analytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + analytics: analytics) //When sut.disconnectCardReader() @@ -1288,17 +917,10 @@ struct PointOfSaleAggregateModelTests { @Test func checkout_when_invoked_then_tracks_trackCheckoutTapped() async throws { // Given let analyticsTracker = MockPOSCollectOrderPaymentAnalyticsTracker() - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: cardPresentPaymentService, - orderController: orderController, - collectOrderPaymentAnalyticsTracker: analyticsTracker, - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel( + cardPresentPaymentService: cardPresentPaymentService, + orderController: orderController, + collectOrderPaymentAnalyticsTracker: analyticsTracker) // When await sut.checkOut() @@ -1311,18 +933,9 @@ struct PointOfSaleAggregateModelTests { @Test func cancelCashPayment_when_invoked_then_tracks_expected_event() async throws { // Given let analyticsTracker = MockPOSCollectOrderPaymentAnalyticsTracker() - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: analytics, - collectOrderPaymentAnalyticsTracker: analyticsTracker, - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel( + analytics: analytics, + collectOrderPaymentAnalyticsTracker: analyticsTracker) // When await sut.cancelCashPayment() @@ -1335,18 +948,7 @@ struct PointOfSaleAggregateModelTests { // Given let mockAnalyticsProvider = MockAnalyticsProvider() let mockAnalytics = WooAnalytics(analyticsProvider: mockAnalyticsProvider) - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: mockAnalytics, - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService(), - soundPlayer: MockPointOfSaleSoundPlayer()) + let sut = makePointOfSaleAggregateModel(analytics: mockAnalytics) // When await sut.startCashPayment() @@ -1362,18 +964,9 @@ struct PointOfSaleAggregateModelTests { // Given let soundPlayer = MockPointOfSaleSoundPlayer() let barcodeScanService = MockPointOfSaleBarcodeScanService() - let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: barcodeScanService, - soundPlayer: soundPlayer) + let sut = makePointOfSaleAggregateModel( + barcodeScanService: barcodeScanService, + soundPlayer: soundPlayer) barcodeScanService.errorToThrow = .notFound(scannedCode: "123456") // When & Then @@ -1414,3 +1007,38 @@ private func makeLoadedOrderState(cartTotal: String = "", order ) } + +@available(iOS 17.0, *) +private func makePointOfSaleAggregateModel( + entryPointController: POSEntryPointController = POSEntryPointController(eligibilityChecker: MockPOSEligibilityChecker()), + itemsController: PointOfSaleItemsControllerProtocol = MockPointOfSaleItemsController(), + purchasableItemsSearchController: PointOfSaleSearchingItemsControllerProtocol = MockPointOfSalePurchasableItemsSearchController(), + couponsController: PointOfSaleCouponsControllerProtocol = MockPointOfSaleCouponsController(), + couponsSearchController: PointOfSaleSearchingItemsControllerProtocol = MockPointOfSaleCouponsController(), + cardPresentPaymentService: CardPresentPaymentFacade = MockCardPresentPaymentService(), + orderController: PointOfSaleOrderControllerProtocol = MockPointOfSaleOrderController(), + analytics: Analytics = WooAnalytics(analyticsProvider: MockAnalyticsProvider()), + collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking = MockPOSCollectOrderPaymentAnalyticsTracker(), + searchHistoryService: POSSearchHistoryProviding = MockPOSSearchHistoryService(), + popularPurchasableItemsController: PointOfSaleItemsControllerProtocol = MockPointOfSaleItemsController(), + barcodeScanService: PointOfSaleBarcodeScanServiceProtocol = MockPointOfSaleBarcodeScanService(), + soundPlayer: PointOfSaleSoundPlayerProtocol = MockPointOfSaleSoundPlayer(), + paymentState: PointOfSalePaymentState = .idle +) -> PointOfSaleAggregateModel { + PointOfSaleAggregateModel( + entryPointController: entryPointController, + itemsController: itemsController, + purchasableItemsSearchController: purchasableItemsSearchController, + couponsController: couponsController, + couponsSearchController: couponsSearchController, + cardPresentPaymentService: cardPresentPaymentService, + orderController: orderController, + analytics: analytics, + collectOrderPaymentAnalyticsTracker: collectOrderPaymentAnalyticsTracker, + searchHistoryService: searchHistoryService, + popularPurchasableItemsController: popularPurchasableItemsController, + barcodeScanService: barcodeScanService, + soundPlayer: soundPlayer, + paymentState: paymentState + ) +} diff --git a/WooCommerce/WooCommerceTests/POS/Presentation/POSItemActionHandlerTests.swift b/WooCommerce/WooCommerceTests/POS/Presentation/POSItemActionHandlerTests.swift index 3c859809116..2fece6e897b 100644 --- a/WooCommerce/WooCommerceTests/POS/Presentation/POSItemActionHandlerTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Presentation/POSItemActionHandlerTests.swift @@ -2,22 +2,14 @@ import Testing import Foundation import enum Yosemite.POSItem import enum Yosemite.POSItemType +import protocol Yosemite.PointOfSaleBarcodeScanServiceProtocol import protocol Yosemite.POSSearchHistoryProviding @testable import WooCommerce struct POSItemActionHandlerTests { @available(iOS 17.0, *) @Test func handleTap_when_attempt_to_add_duplicated_coupons_in_list_then_does_not_add_it_to_cart() async throws { - let aggregateModel = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService()) + let aggregateModel = makePointOfSaleAggregateModel() let sut = StandardPOSItemActionHandler( posModel: aggregateModel, sourceView: .coupon, @@ -35,16 +27,7 @@ struct POSItemActionHandlerTests { @available(iOS 17.0, *) @Test func handleTap_when_attempt_to_add_duplicated_coupons_in_search_then_does_not_add_it_to_cart() async throws { - let aggregateModel = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService()) + let aggregateModel = makePointOfSaleAggregateModel() let sut = SearchResultItemActionHandler( posModel: aggregateModel, searchTerm: "", @@ -63,16 +46,7 @@ struct POSItemActionHandlerTests { @available(iOS 17.0, *) @Test func handleTap_when_attempt_to_add_duplicated_products_in_list_then_adds_them_to_cart() async throws { - let aggregateModel = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService()) + let aggregateModel = makePointOfSaleAggregateModel() let sut = StandardPOSItemActionHandler( posModel: aggregateModel, sourceView: .product, @@ -90,16 +64,7 @@ struct POSItemActionHandlerTests { @available(iOS 17.0, *) @Test func handleTap_when_attempt_to_add_duplicated_products_in_search_then_adds_them_to_cart() async throws { - let aggregateModel = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(), - couponsController: MockPointOfSaleCouponsController(), - couponsSearchController: MockPointOfSaleCouponsController(), - cardPresentPaymentService: MockCardPresentPaymentService(), - orderController: MockPointOfSaleOrderController(), - collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), - searchHistoryService: MockPOSSearchHistoryService(), - popularPurchasableItemsController: MockPointOfSaleItemsController(), - barcodeScanService: MockPointOfSaleBarcodeScanService()) + let aggregateModel = makePointOfSaleAggregateModel() let sut = SearchResultItemActionHandler( posModel: aggregateModel, searchTerm: "", @@ -131,3 +96,32 @@ private func makeProductItem() -> POSItem { stockQuantity: nil, stockStatusKey: "")) } + +@available(iOS 17.0, *) +private func makePointOfSaleAggregateModel( + entryPointController: POSEntryPointController = POSEntryPointController(eligibilityChecker: MockPOSEligibilityChecker()), + itemsController: PointOfSaleItemsControllerProtocol = MockPointOfSaleItemsController(), + purchasableItemsSearchController: PointOfSaleSearchingItemsControllerProtocol = MockPointOfSalePurchasableItemsSearchController(), + couponsController: PointOfSaleCouponsControllerProtocol = MockPointOfSaleCouponsController(), + couponsSearchController: PointOfSaleSearchingItemsControllerProtocol = MockPointOfSaleCouponsController(), + cardPresentPaymentService: CardPresentPaymentFacade = MockCardPresentPaymentService(), + orderController: PointOfSaleOrderControllerProtocol = MockPointOfSaleOrderController(), + collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking = MockPOSCollectOrderPaymentAnalyticsTracker(), + searchHistoryService: POSSearchHistoryProviding = MockPOSSearchHistoryService(), + popularPurchasableItemsController: PointOfSaleItemsControllerProtocol = MockPointOfSaleItemsController(), + barcodeScanService: PointOfSaleBarcodeScanServiceProtocol = MockPointOfSaleBarcodeScanService() +) -> PointOfSaleAggregateModel { + PointOfSaleAggregateModel( + entryPointController: entryPointController, + itemsController: itemsController, + purchasableItemsSearchController: purchasableItemsSearchController, + couponsController: couponsController, + couponsSearchController: couponsSearchController, + cardPresentPaymentService: cardPresentPaymentService, + orderController: orderController, + collectOrderPaymentAnalyticsTracker: collectOrderPaymentAnalyticsTracker, + searchHistoryService: searchHistoryService, + popularPurchasableItemsController: popularPurchasableItemsController, + barcodeScanService: barcodeScanService + ) +} diff --git a/WooCommerce/WooCommerceTests/POS/ViewHelpers/PointOfSaleDashboardViewHelperTests.swift b/WooCommerce/WooCommerceTests/POS/ViewHelpers/PointOfSaleDashboardViewHelperTests.swift new file mode 100644 index 00000000000..ef035f6fdb0 --- /dev/null +++ b/WooCommerce/WooCommerceTests/POS/ViewHelpers/PointOfSaleDashboardViewHelperTests.swift @@ -0,0 +1,240 @@ +import Foundation +import SwiftUI +import Testing +import enum WooFoundationCore.CurrencyCode +@testable import WooCommerce + +struct PointOfSaleDashboardViewHelperTests { + // MARK: - Horizontal Size Class Tests + + @available(iOS 17.0, *) + @Test func determineViewState_when_horizontalSizeClass_is_compact_returns_unsupportedWidth() async throws { + // Given + let eligibilityState: POSEligibilityState = .eligible + let itemsContainerState: ItemsContainerState = .content + let horizontalSizeClass: UserInterfaceSizeClass = .compact + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .unsupportedWidth) + } + + @available(iOS 17.0, *) + @Test func determineViewState_when_horizontalSizeClass_is_nil_returns_unsupportedWidth() async throws { + // Given + let eligibilityState: POSEligibilityState = .eligible + let itemsContainerState: ItemsContainerState = .content + let horizontalSizeClass: UserInterfaceSizeClass? = nil + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .unsupportedWidth) + } + + // MARK: - Eligibility State Tests + + @available(iOS 17.0, *) + @Test func determineViewState_when_eligibilityState_is_nil_returns_loading() async throws { + // Given + let eligibilityState: POSEligibilityState? = nil + let itemsContainerState: ItemsContainerState = .content + let horizontalSizeClass: UserInterfaceSizeClass = .regular + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .loading) + } + + @available(iOS 17.0, *) + @Test(arguments: [ + POSIneligibleReason.unsupportedIOSVersion, + POSIneligibleReason.unsupportedWooCommerceVersion(minimumVersion: "9.6.0"), + POSIneligibleReason.unsupportedCurrency(supportedCurrencies: [.USD, .GBP]), + POSIneligibleReason.siteSettingsNotAvailable, + POSIneligibleReason.wooCommercePluginNotFound, + POSIneligibleReason.featureSwitchDisabled, + POSIneligibleReason.selfDeallocated + ]) + func determineViewState_when_ineligible_returns_ineligible(reason: POSIneligibleReason) async throws { + // Given + let eligibilityState: POSEligibilityState = .ineligible(reason: reason) + let itemsContainerState: ItemsContainerState = .content + let horizontalSizeClass: UserInterfaceSizeClass = .regular + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .ineligible(reason: reason)) + } + + // MARK: - Eligible State Tests + + @available(iOS 17.0, *) + @Test func determineViewState_when_eligible_and_loading_returns_loading() async throws { + // Given + let eligibilityState: POSEligibilityState = .eligible + let itemsContainerState: ItemsContainerState = .loading + let horizontalSizeClass: UserInterfaceSizeClass = .regular + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .loading) + } + + @available(iOS 17.0, *) + @Test func determineViewState_when_eligible_and_content_returns_content() async throws { + // Given + let eligibilityState: POSEligibilityState = .eligible + let itemsContainerState: ItemsContainerState = .content + let horizontalSizeClass: UserInterfaceSizeClass = .regular + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .content) + } + + // MARK: - Error State Tests + + @available(iOS 17.0, *) + @Test(arguments: [ + PointOfSaleErrorState.errorOnLoadingProducts(), + PointOfSaleErrorState.errorOnLoadingVariations(), + PointOfSaleErrorState.errorOnLoadingCoupons(), + PointOfSaleErrorState.errorCouponsDisabled, + PointOfSaleErrorState.errorOnLoadingProductsNextPage(), + PointOfSaleErrorState.errorOnLoadingVariationsNextPage(), + PointOfSaleErrorState.errorOnLoadingCouponsNextPage(), + PointOfSaleErrorState.errorOnRefreshingCoupons(), + PointOfSaleErrorState.errorOnEnablingCoupons() + ]) + func determineViewState_when_eligible_and_error_returns_error(errorState: PointOfSaleErrorState) async throws { + // Given + let eligibilityState: POSEligibilityState = .eligible + let itemsContainerState: ItemsContainerState = .error(errorState) + let horizontalSizeClass: UserInterfaceSizeClass = .regular + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .error(errorState)) + } + + // MARK: - Priority Tests + + @available(iOS 17.0, *) + @Test func determineViewState_horizontalSizeClass_takes_priority_over_eligibility_state() async throws { + // Given - compact size class should return unsupportedWidth regardless of eligibility + let eligibilityState: POSEligibilityState = .ineligible(reason: .unsupportedIOSVersion) + let itemsContainerState: ItemsContainerState = .content + let horizontalSizeClass: UserInterfaceSizeClass = .compact + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .unsupportedWidth) + } + + @available(iOS 17.0, *) + @Test func determineViewState_nil_eligibilityState_takes_priority_over_containerState() async throws { + // Given - nil eligibility should return loading regardless of container state + let eligibilityState: POSEligibilityState? = nil + let itemsContainerState: ItemsContainerState = .content + let horizontalSizeClass: UserInterfaceSizeClass = .regular + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .loading) + } + + @available(iOS 17.0, *) + @Test func determineViewState_ineligible_state_takes_priority_over_containerState() async throws { + // Given - ineligible state should return ineligible regardless of container state + let eligibilityState: POSEligibilityState = .ineligible(reason: .unsupportedIOSVersion) + let itemsContainerState: ItemsContainerState = .content + let horizontalSizeClass: UserInterfaceSizeClass = .regular + + // When + let result = PointOfSaleDashboardViewHelper.determineViewState( + eligibilityState: eligibilityState, + itemsContainerState: itemsContainerState, + horizontalSizeClass: horizontalSizeClass + ) + + // Then + #expect(result == .ineligible(reason: .unsupportedIOSVersion)) + } + + // MARK: - Floating Control Tests + + @available(iOS 17.0, *) + @Test(arguments: [ + (PointOfSaleDashboardView.ViewState.content, true), + (PointOfSaleDashboardView.ViewState.error(PointOfSaleErrorState.errorOnLoadingProducts()), true), + (PointOfSaleDashboardView.ViewState.unsupportedWidth, true) + ]) + func showsFloatingControl_when_content_error_or_unsupportedWidth_returns_true(viewState: PointOfSaleDashboardView.ViewState, expected: Bool) async throws { + // When & Then + #expect(viewState.showsFloatingControl == expected) + } + + @available(iOS 17.0, *) + @Test(arguments: [ + (PointOfSaleDashboardView.ViewState.loading, false), + (PointOfSaleDashboardView.ViewState.ineligible(reason: .unsupportedIOSVersion), false) + ]) + func showsFloatingControl_when_loading_or_ineligible_returns_false(viewState: PointOfSaleDashboardView.ViewState, expected: Bool) async throws { + // When & Then + #expect(viewState.showsFloatingControl == expected) + } +}