diff --git a/Modules/Sources/Experiments/FeatureFlag.swift b/Modules/Sources/Experiments/FeatureFlag.swift index 328ffb56434..8ad4b72be8b 100644 --- a/Modules/Sources/Experiments/FeatureFlag.swift +++ b/Modules/Sources/Experiments/FeatureFlag.swift @@ -131,8 +131,7 @@ public enum FeatureFlag: Int { /// case subscriptionsInOrderCreationCustomers - /// Enables the Point Of Sale entry point under the Menu tab, if all requirements pass. - /// Please check the `POSEligibilityChecker` class for additional requirements in order to enable the feature. + /// Enables the Point Of Sale when remote feature flag is disabled. /// case pointOfSale diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSEligibilityChecker.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSEligibilityChecker.swift deleted file mode 100644 index 77050421bdf..00000000000 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSEligibilityChecker.swift +++ /dev/null @@ -1,160 +0,0 @@ -import Combine -import Foundation -import UIKit -import class WooFoundation.CurrencySettings -import enum WooFoundation.CountryCode -import protocol Experiments.FeatureFlagService -import struct Yosemite.SiteSetting -import protocol Yosemite.StoresManager -import enum Yosemite.SystemStatusAction -import enum Yosemite.FeatureFlagAction -import enum Yosemite.SettingAction - -protocol POSEligibilityCheckerProtocol { - /// As POS eligibility can change from site settings and card payment onboarding state, it's recommended to observe the eligibility value. - var isEligible: AnyPublisher { get } -} - -/// Determines whether the POS entry point can be shown based on the selected store and feature gates. -final class POSEligibilityChecker: POSEligibilityCheckerProtocol { - var isEligible: AnyPublisher { - // Conditions that are fixed for its lifetime. - let isTablet = userInterfaceIdiom == .pad - guard isTablet, - #available(iOS 17.0, *) else { - return Just(false) - .eraseToAnyPublisher() - } - - return Publishers.CombineLatest(isWooCommerceVersionSupportedAndFeatureSwitchEnabled, isPointOfSaleFeatureFlagEnabled) - .filter { [weak self] _ in - self?.isEligibleFromSiteChecks ?? false - } - .map { $0 && $1 } - .eraseToAnyPublisher() - } - - private let userInterfaceIdiom: UIUserInterfaceIdiom - private let siteSettings: SelectedSiteSettings - private let currencySettings: CurrencySettings - private let stores: StoresManager - private let featureFlagService: FeatureFlagService - - init(userInterfaceIdiom: UIUserInterfaceIdiom = UIDevice.current.userInterfaceIdiom, - siteSettings: SelectedSiteSettings = ServiceLocator.selectedSiteSettings, - currencySettings: CurrencySettings = ServiceLocator.currencySettings, - stores: StoresManager = ServiceLocator.stores, - featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService) { - self.userInterfaceIdiom = userInterfaceIdiom - self.siteSettings = siteSettings - self.currencySettings = currencySettings - self.stores = stores - self.featureFlagService = featureFlagService - } -} - -private extension POSEligibilityChecker { - var isWooCommerceVersionSupported: AnyPublisher<(isSupported: Bool, wcVersion: String?), Never> { - Future<(isSupported: Bool, wcVersion: String?), Never> { [weak self] promise in - guard let self, - let siteID = self.stores.sessionManager.defaultStoreID else { - promise(.success((isSupported: false, wcVersion: nil))) - return - } - - let wcPluginMinimumVersion = Constants.wcPluginMinimumVersion - - let action = SystemStatusAction.fetchSystemPlugin(siteID: siteID, systemPluginName: Constants.wcPluginName) { wcPlugin in - guard let wcPlugin, wcPlugin.active else { - return promise(.success((isSupported: false, wcVersion: nil))) - } - - let isSupported = VersionHelpers.isVersionSupported(version: wcPlugin.version, - minimumRequired: wcPluginMinimumVersion) - promise(.success((isSupported: isSupported, wcVersion: wcPlugin.version))) - } - self.stores.dispatch(action) - } - .eraseToAnyPublisher() - } - - var isWooCommerceVersionSupportedAndFeatureSwitchEnabled: AnyPublisher { - isWooCommerceVersionSupported - .flatMap { [weak self] isSupported, wcVersion -> AnyPublisher in - guard let self, - isSupported, - let wcVersion, - let siteID = self.stores.sessionManager.defaultStoreID else { - return Just(false).eraseToAnyPublisher() - } - - // For versions below 10.0.0, the feature is enabled by default. - let isFeatureSwitchSupported = VersionHelpers.isVersionSupported(version: wcVersion, - minimumRequired: Constants.wcPluginMinimumVersionWithFeatureSwitch, - includesDevAndBetaVersions: true) - if !isFeatureSwitchSupported { - return Just(true).eraseToAnyPublisher() - } - - // For versions that support the feature switch, checks if the feature switch is enabled. - return Future { promise in - let action = SettingAction.isFeatureEnabled(siteID: siteID, feature: .pointOfSale) { result in - switch result { - case .success(let isEnabled): - promise(.success(isEnabled)) - case .failure: - promise(.success(false)) - } - } - self.stores.dispatch(action) - } - .eraseToAnyPublisher() - } - .eraseToAnyPublisher() - } - - var isPointOfSaleFeatureFlagEnabled: AnyPublisher { - // Only whitelisted accounts in WPCOM have the Point of Sale remote feature flag enabled. These can be found at D159901-code - // If the account is whitelisted, then the remote value takes preference over the local feature flag configuration - Future { [weak self] promise in - guard let self else { - promise(.success(false)) - return - } - let action = FeatureFlagAction.isRemoteFeatureFlagEnabled(.pointOfSale, defaultValue: false, completion: { result in - switch result { - case true: - // The site is whitelisted - return promise(.success(true)) - case false: - // When the site is not whitelisted, check the local feature flag configuration - let localFeatureFlag = self.featureFlagService.isFeatureFlagEnabled(.pointOfSale) - return promise(.success(localFeatureFlag)) - } - }) - self.stores.dispatch(action) - } - .eraseToAnyPublisher() - } - - var isEligibleFromSiteChecks: Bool { - // Conditions that can change if site settings are synced during the lifetime. - let countryCode = SiteAddress(siteSettings: siteSettings.siteSettings).countryCode - let currency = currencySettings.currencyCode - switch (countryCode, currency) { - case (.US, .USD), - (.GB, .GBP): - return true - default: - return false - } - } -} - -private extension POSEligibilityChecker { - enum Constants { - static let wcPluginName = "WooCommerce" - static let wcPluginMinimumVersion = "9.6.0-beta" - static let wcPluginMinimumVersionWithFeatureSwitch = "10.0.0" - } -} diff --git a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenu.swift b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenu.swift index 4a9d96886c5..105c276e8e2 100644 --- a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenu.swift +++ b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenu.swift @@ -31,46 +31,6 @@ struct HubMenu: View { .onAppear { viewModel.setupMenuElements() } - .fullScreenCover(isPresented: $viewModel.showsPOS) { - if let cardPresentPaymentService = viewModel.cardPresentPaymentService, - let receiptService = POSReceiptService(siteID: viewModel.siteID, - credentials: viewModel.credentials), - let orderService = POSOrderService(siteID: viewModel.siteID, - credentials: viewModel.credentials), - #available(iOS 17.0, *) { - PointOfSaleEntryPointView( - itemsController: PointOfSaleItemsController( - itemProvider: PointOfSaleItemService( - currencySettings: ServiceLocator.currencySettings), - itemFetchStrategyFactory: viewModel.posItemFetchStrategyFactory), - purchasableItemsSearchController: PointOfSaleItemsController( - itemProvider: PointOfSaleItemService( - currencySettings: ServiceLocator.currencySettings), - itemFetchStrategyFactory: viewModel.posItemFetchStrategyFactory, - initialState: .init(containerState: .content, - itemsStack: .init(root: .loaded([], hasMoreItems: true), itemStates: [:]))), - couponsController: PointOfSaleCouponsController(itemProvider: viewModel.posCouponProvider, - fetchStrategyFactory: viewModel.posCouponFetchStrategyFactory), - couponsSearchController: PointOfSaleCouponsController(itemProvider: viewModel.posCouponProvider, - fetchStrategyFactory: viewModel.posCouponFetchStrategyFactory), - onPointOfSaleModeActiveStateChange: { isEnabled in - viewModel.updateDefaultConfigurationForPointOfSale(isEnabled) - }, - cardPresentPaymentService: cardPresentPaymentService, - orderController: PointOfSaleOrderController(orderService: orderService, - receiptService: receiptService), - collectOrderPaymentAnalyticsTracker: viewModel.collectOrderPaymentAnalyticsTracker, - searchHistoryService: POSSearchHistoryService(siteID: viewModel.siteID), - popularPurchasableItemsController: PointOfSaleItemsController( - itemProvider: PointOfSaleItemService(currencySettings: ServiceLocator.currencySettings), - itemFetchStrategyFactory: viewModel.posPopularItemFetchStrategyFactory), - barcodeScanService: viewModel.barcodeScanService, - posEligibilityChecker: LegacyPOSTabEligibilityChecker(siteID: viewModel.siteID)) - } else { - // TODO: When we have a singleton for the card payment service, this should not be required. - Text("Error creating card payment service") - } - } } } @@ -86,8 +46,6 @@ struct HubMenu: View { ServiceLocator.analytics.track(.hubMenuSettingsTapped) case HubMenuViewModel.Blaze.id: ServiceLocator.analytics.track(event: .Blaze.blazeCampaignListEntryPointSelected(source: .menu)) - case HubMenuViewModel.PointOfSaleEntryPoint.id: - viewModel.showsPOS = true default: break } diff --git a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewModel.swift b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewModel.swift index 15ed4ce4348..4bcc2dbb226 100644 --- a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewModel.swift @@ -82,14 +82,10 @@ final class HubMenuViewModel: ObservableObject { @Published private(set) var hasGoogleAdsCampaigns = false @Published private var currentSite: Yosemite.Site? - /// Whether the app is in POS mode for an eligible site. - @Published var showsPOS: Bool = false - private let stores: StoresManager private let featureFlagService: FeatureFlagService private let generalAppSettings: GeneralAppSettingsStorage private let cardPresentPaymentsOnboarding: CardPresentPaymentsOnboardingUseCaseProtocol - private let posEligibilityChecker: POSEligibilityCheckerProtocol private let inboxEligibilityChecker: InboxEligibilityChecker private let blazeEligibilityChecker: BlazeEligibilityCheckerProtocol private let googleAdsEligibilityChecker: GoogleAdsEligibilityChecker @@ -177,9 +173,6 @@ final class HubMenuViewModel: ObservableObject { self.blazeEligibilityChecker = blazeEligibilityChecker self.googleAdsEligibilityChecker = googleAdsEligibilityChecker self.cardPresentPaymentsOnboarding = CardPresentPaymentsOnboardingUseCase() - self.posEligibilityChecker = POSEligibilityChecker(siteSettings: ServiceLocator.selectedSiteSettings, - currencySettings: ServiceLocator.currencySettings, - featureFlagService: featureFlagService) self.analytics = analytics observeSiteForUIUpdates() observePlanName() @@ -210,7 +203,6 @@ final class HubMenuViewModel: ObservableObject { /// Resets the menu elements displayed on the menu. /// func setupMenuElements() { - setupPOSElement() setupSettingsElements() setupGeneralElements() } @@ -250,14 +242,7 @@ final class HubMenuViewModel: ObservableObject { } func trackMenuItemTapEvent(menu: HubMenuItem) { - let eventProperties: [AnyHashable: Any] = { - var properties: [AnyHashable: Any] = [AnalyticsKeys.trackingOption: menu.trackingOption] - if menu.id == HubMenuViewModel.PointOfSaleEntryPoint.id { - properties[AnalyticsKeys.paymentsOnboardingState] = cardPresentPaymentsOnboarding.state.reasonForAnalytics - } - return properties - }() - analytics.track(.hubMenuOptionTapped, withProperties: eventProperties) + analytics.track(.hubMenuOptionTapped, withProperties: [AnalyticsKeys.trackingOption: menu.trackingOption]) } func createGoogleAdsCampaignCoordinator(with navigationController: UINavigationController) -> GoogleAdsCampaignCoordinator { @@ -313,21 +298,6 @@ private extension HubMenuViewModel { } } - func setupPOSElement() { - guard featureFlagService.isFeatureFlagEnabled(.pointOfSaleAsATabi1) == false else { - return - } - - posEligibilityChecker.isEligible.map { isEligibleForPOS in - if isEligibleForPOS { - return PointOfSaleEntryPoint() - } else { - return nil - } - } - .assign(to: &$posElement) - } - func setupSettingsElements() { settingsElements = [Settings()] @@ -694,20 +664,6 @@ extension HubMenuViewModel { let navigationDestination: HubMenuNavigationDestination? = .reviews } - struct PointOfSaleEntryPoint: HubMenuItem { - static var id = "pointOfSale" - - let title: String = Localization.pos - let description: String = Localization.posDescription - let icon: UIImage = .pointOfSaleImage - let iconColor: UIColor = .withColorStudio(.green, shade: .shade30) - let accessibilityIdentifier: String = "menu-pointOfSale" - let trackingOption: String = "pointOfSale" - let iconBadge: HubMenuBadgeType? = nil - // POS is presented with its own navigation stack as nested navigation stack is not supported. - let navigationDestination: HubMenuNavigationDestination? = nil - } - struct Subscriptions: HubMenuItem { static var id = "subscriptions" @@ -785,15 +741,6 @@ extension HubMenuViewModel { "My Store", comment: "Title of the hub menu view in case there is no title for the store") - static let pos = NSLocalizedString( - "Point of Sale Mode", - comment: "Title of the POS menu in the hub menu") - - static let posDescription = NSLocalizedString( - "hubMenu.pointOfSale.description", - value: "Accept payments at your physical store", - comment: "Description of the POS menu in the hub menu") - static let woocommerceAdmin = NSLocalizedString( "WooCommerce Admin", comment: "Title of one of the hub menu options") @@ -855,7 +802,6 @@ extension HubMenuViewModel { enum AnalyticsKeys { static let trackingOption = "option" - static let paymentsOnboardingState = "payments_onboarding_state" } } diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 0e884841456..53273814419 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -239,7 +239,6 @@ 023BD5842BFDCBF800A10D7B /* BetaFeaturesConfigurationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023BD5832BFDCBF800A10D7B /* BetaFeaturesConfigurationViewModel.swift */; }; 023BD5862BFDCECF00A10D7B /* BetaFeaturesConfigurationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023BD5852BFDCECF00A10D7B /* BetaFeaturesConfigurationViewModelTests.swift */; }; 023BD5882BFDCF3100A10D7B /* MockInMemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023BD5872BFDCF3100A10D7B /* MockInMemoryStorage.swift */; }; - 023BD58B2BFDCFCB00A10D7B /* POSEligibilityCheckerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023BD58A2BFDCFCB00A10D7B /* POSEligibilityCheckerTests.swift */; }; 023D1DD124AB2D05002B03A3 /* ProductListSelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023D1DD024AB2D05002B03A3 /* ProductListSelectorViewController.swift */; }; 023D692E2588BF0900F7DA72 /* ShippingLabelPaperSizeListSelectorCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023D692D2588BF0900F7DA72 /* ShippingLabelPaperSizeListSelectorCommand.swift */; }; 023D69442588C6BD00F7DA72 /* ShippingLabelPaperSizeListSelectorCommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023D69432588C6BD00F7DA72 /* ShippingLabelPaperSizeListSelectorCommandTests.swift */; }; @@ -609,7 +608,6 @@ 02E4908929AE49B9005942AE /* TopPerformersEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E4908829AE49B9005942AE /* TopPerformersEmptyView.swift */; }; 02E4908D29AF216E005942AE /* TopPerformersPeriodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E4908C29AF216E005942AE /* TopPerformersPeriodView.swift */; }; 02E493EF245C1087000AEA9E /* ProductFormBottomSheetListSelectorCommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E493EE245C1087000AEA9E /* ProductFormBottomSheetListSelectorCommandTests.swift */; }; - 02E4A0832BFB1C4F006D4F87 /* POSEligibilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E4A0822BFB1C4F006D4F87 /* POSEligibilityChecker.swift */; }; 02E4AF7126FC4F16002AD9F4 /* ProductReviewFromNoteParcelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E4AF7026FC4F16002AD9F4 /* ProductReviewFromNoteParcelFactory.swift */; }; 02E4E7442E0EEF80003A31E7 /* POSIneligibleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E4E7432E0EEF76003A31E7 /* POSIneligibleView.swift */; }; 02E4E7462E0EF84B003A31E7 /* POSEntryPointController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E4E7452E0EF847003A31E7 /* POSEntryPointController.swift */; }; @@ -3417,7 +3415,6 @@ 023BD5832BFDCBF800A10D7B /* BetaFeaturesConfigurationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetaFeaturesConfigurationViewModel.swift; sourceTree = ""; }; 023BD5852BFDCECF00A10D7B /* BetaFeaturesConfigurationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetaFeaturesConfigurationViewModelTests.swift; sourceTree = ""; }; 023BD5872BFDCF3100A10D7B /* MockInMemoryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockInMemoryStorage.swift; sourceTree = ""; }; - 023BD58A2BFDCFCB00A10D7B /* POSEligibilityCheckerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSEligibilityCheckerTests.swift; sourceTree = ""; }; 023D1DD024AB2D05002B03A3 /* ProductListSelectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductListSelectorViewController.swift; sourceTree = ""; }; 023D692D2588BF0900F7DA72 /* ShippingLabelPaperSizeListSelectorCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelPaperSizeListSelectorCommand.swift; sourceTree = ""; }; 023D69432588C6BD00F7DA72 /* ShippingLabelPaperSizeListSelectorCommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelPaperSizeListSelectorCommandTests.swift; sourceTree = ""; }; @@ -3789,7 +3786,6 @@ 02E4908829AE49B9005942AE /* TopPerformersEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopPerformersEmptyView.swift; sourceTree = ""; }; 02E4908C29AF216E005942AE /* TopPerformersPeriodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopPerformersPeriodView.swift; sourceTree = ""; }; 02E493EE245C1087000AEA9E /* ProductFormBottomSheetListSelectorCommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductFormBottomSheetListSelectorCommandTests.swift; sourceTree = ""; }; - 02E4A0822BFB1C4F006D4F87 /* POSEligibilityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSEligibilityChecker.swift; sourceTree = ""; }; 02E4AF7026FC4F16002AD9F4 /* ProductReviewFromNoteParcelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductReviewFromNoteParcelFactory.swift; sourceTree = ""; }; 02E4E7432E0EEF76003A31E7 /* POSIneligibleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSIneligibleView.swift; sourceTree = ""; }; 02E4E7452E0EF847003A31E7 /* POSEntryPointController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSEntryPointController.swift; sourceTree = ""; }; @@ -6833,7 +6829,6 @@ 023BD5892BFDCF9500A10D7B /* POS */ = { isa = PBXGroup; children = ( - 023BD58A2BFDCFCB00A10D7B /* POSEligibilityCheckerTests.swift */, 0277889D2DF928E3006F5B8C /* POSTabEligibilityCheckerTests.swift */, 02B881842E18586B009375F5 /* LegacyPOSTabEligibilityCheckerTests.swift */, ); @@ -7807,7 +7802,6 @@ isa = PBXGroup; children = ( 026B2D162DF92290005B8CAA /* POSTabEligibilityChecker.swift */, - 02E4A0822BFB1C4F006D4F87 /* POSEligibilityChecker.swift */, 02B881822E1857DF009375F5 /* LegacyPOSTabEligibilityChecker.swift */, ); path = POS; @@ -16483,7 +16477,6 @@ B57C744E20F56E3800EEFC87 /* UITableViewCell+Helpers.swift in Sources */, 0295355B245ADF8100BDC42B /* FilterType+Products.swift in Sources */, 02CA63DA23D1ADD100BBF148 /* CameraCaptureCoordinator.swift in Sources */, - 02E4A0832BFB1C4F006D4F87 /* POSEligibilityChecker.swift in Sources */, 260C31602524ECA900157BC2 /* IssueRefundViewController.swift in Sources */, 4521397027FF53E400964ED3 /* CouponExpiryDateView.swift in Sources */, EE4C45812C36E769001A3D94 /* ViewPackagePhoto.swift in Sources */, @@ -17617,7 +17610,6 @@ CC923A1D2847A8E0008EEEBE /* OrderStatusListViewModelTests.swift in Sources */, 267C01D129E9B18E00FCC97B /* StorePlanSynchronizerTests.swift in Sources */, EE45E2C42A4A85350085F227 /* TooltipPresenterTests.swift in Sources */, - 023BD58B2BFDCFCB00A10D7B /* POSEligibilityCheckerTests.swift in Sources */, 0388E1A629E04433007DF84D /* PaymentsRouteTests.swift in Sources */, EEC5C8DA2ADE2FD80071E852 /* BlazeCampaignDashboardViewModelTests.swift in Sources */, D85B833D2230DC9D002168F3 /* StringWooTests.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/ViewRelated/HubMenu/HubMenuViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/HubMenu/HubMenuViewModelTests.swift index 198da50a025..fad691722d4 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/HubMenu/HubMenuViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/HubMenu/HubMenuViewModelTests.swift @@ -730,24 +730,6 @@ final class HubMenuViewModelTests: XCTestCase { XCTAssertTrue(viewModel.hasGoogleAdsCampaigns) } - @MainActor - func test_trackMenuItemTapEvent_includes_payments_onboarding_state_for_pos_menu_item() throws { - // Given - let analyticsProvider = MockAnalyticsProvider() - let analytics = WooAnalytics(analyticsProvider: analyticsProvider) - let viewModel = HubMenuViewModel(siteID: sampleSiteID, - tapToPayBadgePromotionChecker: TapToPayBadgePromotionChecker(), - analytics: analytics) - - // When - viewModel.trackMenuItemTapEvent(menu: HubMenuViewModel.PointOfSaleEntryPoint()) - - // Then - XCTAssertNotNil(analyticsProvider.receivedEvents.first(where: { $0 == "hub_menu_option_tapped" })) - let eventProperties = try XCTUnwrap(analyticsProvider.receivedProperties.first(where: { $0.keys.contains("payments_onboarding_state") })) - XCTAssertNotNil(eventProperties["payments_onboarding_state"]) - } - @MainActor func test_trackMenuItemTapEvent_does_not_include_payments_onboarding_state_for_non_pos_menu_items() throws { // Given diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Settings/POS/POSEligibilityCheckerTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Settings/POS/POSEligibilityCheckerTests.swift deleted file mode 100644 index 098d6ab9441..00000000000 --- a/WooCommerce/WooCommerceTests/ViewRelated/Settings/POS/POSEligibilityCheckerTests.swift +++ /dev/null @@ -1,340 +0,0 @@ -import Combine -import WooFoundation -import XCTest -import Yosemite -@testable import WooCommerce - -final class POSEligibilityCheckerTests: XCTestCase { - private var stores: MockStoresManager! - private var storageManager: MockStorageManager! - private var siteSettings: SelectedSiteSettings! - @Published private var isEligible: Bool = false - - private let siteID: Int64 = 2 - - override func setUp() { - super.setUp() - stores = MockStoresManager(sessionManager: .makeForTesting(authenticated: true)) - stores.updateDefaultStore(storeID: siteID) - setupWooCommerceVersion() - storageManager = MockStorageManager() - siteSettings = SelectedSiteSettings(stores: stores, storageManager: storageManager) - } - - override func tearDown() { - siteSettings = nil - storageManager = nil - stores = nil - super.tearDown() - } - - func test_is_eligible_when_all_conditions_satisfied_then_returns_true() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - accountWhitelistedInBackend(true) - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertTrue(isEligible) - } - - func test_is_eligible_when_account_not_whitelisted_in_backend_and_enabled_via_local_feature_flag_then_returns_true() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - accountWhitelistedInBackend(false) - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertTrue(isEligible) - } - - func test_is_eligible_when_account_not_whitelisted_in_backend_and_not_enabled_via_local_feature_flag_then_returns_false() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: false) - setupCountry(country: .us) - accountWhitelistedInBackend(false) - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertFalse(isEligible) - } - - func test_is_eligible_when_non_iPad_device_then_returns_false() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - [UIUserInterfaceIdiom.phone, UIUserInterfaceIdiom.mac, UIUserInterfaceIdiom.tv, UIUserInterfaceIdiom.carPlay] - .forEach { userInterfaceIdiom in - let checker = POSEligibilityChecker(userInterfaceIdiom: userInterfaceIdiom, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertFalse(isEligible) - } - } - - func test_is_eligible_when_non_us_site_then_returns_false() { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - [Country.ca, Country.es, Country.gb].forEach { country in - // When - setupCountry(country: country) - accountWhitelistedInBackend(true) - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertFalse(isEligible) - } - } - - func test_when_non_usd_currency_then_isEligible_returns_false() { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - accountWhitelistedInBackend(true) - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.nonUSDCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertFalse(isEligible) - } - - func test_is_eligible_when_feature_flag_is_disabled_then_returns_false() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: false) - setupCountry(country: .us) - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertFalse(isEligible) - } - - func test_is_eligible_when_WooCommerce_version_is_below_9_6_then_returns_false() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - - // Unsupported WooCommerce version - setupWooCommerceVersion("9.5.2") - - // When - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertFalse(isEligible) - } - - func test_is_eligible_when_WooCommerce_version_is_at_least_9_6_then_returns_true() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - accountWhitelistedInBackend(true) - - // Supported WooCommerce version - setupWooCommerceVersion("9.6.0-beta1") - - // When - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertTrue(isEligible) - } - - func test_is_eligible_when_core_version_is_10_0_0_and_POS_feature_enabled_then_returns_true() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - accountWhitelistedInBackend(true) - - // WC version 10.0.0 with POS feature enabled. - setupWooCommerceVersion("10.0.0") - setupPOSFeatureEnabled(.success(true)) - - // When - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertTrue(isEligible) - } - - func test_is_eligible_when_core_version_is_10_0_0_and_POS_feature_disabled_then_returns_false() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - accountWhitelistedInBackend(true) - - // WC version 10.0.0 with POS feature disabled. - setupWooCommerceVersion("10.0.0") - setupPOSFeatureEnabled(.success(false)) - - // When - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertFalse(isEligible) - } - - func test_is_eligible_when_core_version_is_10_0_0_and_POS_feature_check_fails_then_returns_false() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - accountWhitelistedInBackend(true) - - // WC version 10.0.0 with POS feature check failing. - setupWooCommerceVersion("10.0.0") - setupPOSFeatureEnabled(.failure(NSError(domain: "test", code: 0))) - - // When - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertFalse(isEligible) - } - - func test_is_eligible_when_core_version_is_smaller_than_10_0_0_and_POS_feature_disabled_then_returns_true() throws { - // Given - let featureFlagService = MockFeatureFlagService(isPointOfSaleEnabled: true) - setupCountry(country: .us) - accountWhitelistedInBackend(true) - - // WC version < 10.0.0 with POS feature disabled. - setupWooCommerceVersion("9.9.9") - setupPOSFeatureEnabled(.success(false)) - - // When - let checker = POSEligibilityChecker(userInterfaceIdiom: .pad, - siteSettings: siteSettings, - currencySettings: Fixtures.usdCurrencySettings, - stores: stores, - featureFlagService: featureFlagService) - checker.isEligible.assign(to: &$isEligible) - - // Then - XCTAssertTrue(isEligible) - } -} - -private extension POSEligibilityCheckerTests { - func setupCountry(country: Country) { - let setting = SiteSetting.fake() - .copy( - siteID: siteID, - settingID: "woocommerce_default_country", - value: country.rawValue, - settingGroupKey: SiteSettingGroup.general.rawValue - ) - storageManager.insertSampleSiteSetting(readOnlySiteSetting: setting) - siteSettings.refresh() - } - - func setupWooCommerceVersion(_ version: String = "9.6.0-beta") { - stores.whenReceivingAction(ofType: SystemStatusAction.self) { action in - switch action { - case .fetchSystemPlugin(_, _, let completion): - completion(SystemPlugin.fake().copy(name: "WooCommerce", version: version, active: true)) - default: - break - } - } - } - - func setupPOSFeatureEnabled(_ result: Result) { - stores.whenReceivingAction(ofType: SettingAction.self) { action in - switch action { - case .isFeatureEnabled(_, _, let completion): - completion(result) - default: - break - } - } - } - - func accountWhitelistedInBackend(_ isAllowed: Bool = false) { - stores.whenReceivingAction(ofType: FeatureFlagAction.self) { action in - switch action { - case .isRemoteFeatureFlagEnabled(_, _, completion: let completion): - completion(isAllowed) - } - } - } - - enum Fixtures { - static let usdCurrencySettings = CurrencySettings(currencyCode: .USD, - currencyPosition: .leftSpace, - thousandSeparator: "", - decimalSeparator: ".", - numberOfDecimals: 3) - static let nonUSDCurrencySettings = CurrencySettings(currencyCode: .CAD, - currencyPosition: .leftSpace, - thousandSeparator: "", - decimalSeparator: ".", - numberOfDecimals: 3) - } - - enum Country: String { - case us = "US:CA" - case ca = "CA:NS" - case gb = "GB" - case es = "ES" - } -}