diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingPlatformsQuestionView.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingPlatformsQuestionView.swift new file mode 100644 index 00000000000..8517f73fd07 --- /dev/null +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingPlatformsQuestionView.swift @@ -0,0 +1,37 @@ +import SwiftUI + +/// Shows the followup question to the store selling status question in the store creation flow, for users who are already online. +/// Displays a list of eCommerce platforms for the user to choose the ones they're already selling on. +struct StoreCreationSellingPlatformsQuestionView: View { + @ObservedObject private var viewModel: StoreCreationSellingPlatformsQuestionViewModel + + init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) { + self.viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip) + } + + var body: some View { + OptionalStoreCreationProfilerQuestionView(viewModel: viewModel) { + VStack(spacing: 16) { + ForEach(viewModel.platforms, id: \.self) { platform in + Button(action: { + viewModel.selectPlatform(platform) + }, label: { + HStack { + Text(platform.description) + Spacer() + } + }) + .buttonStyle(SelectableSecondaryButtonStyle(isSelected: viewModel.selectedPlatforms.contains(platform))) + } + } + } + } +} + +struct StoreCreationSellingPlatformsQuestionView_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + StoreCreationSellingPlatformsQuestionView(storeName: "New Year Store", onContinue: {}, onSkip: {}) + } + } +} diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingPlatformsQuestionViewModel.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingPlatformsQuestionViewModel.swift new file mode 100644 index 00000000000..428a26e9f48 --- /dev/null +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingPlatformsQuestionViewModel.swift @@ -0,0 +1,157 @@ +import Combine +import Foundation + +/// View model for the second step of `StoreCreationSellingStatusQuestionView`, an optional profiler question about store selling status +/// in the store creation flow. +/// When the user previously indicates that they're already selling online, this view model provides data for the followup question on the platforms they're +/// already selling on. +@MainActor +final class StoreCreationSellingPlatformsQuestionViewModel: StoreCreationProfilerQuestionViewModel, ObservableObject { + /// Other online platforms that the user might be selling. Source of truth: + /// https://github.com/Automattic/woocommerce.com/blob/trunk/themes/woo/start/config/options.json + enum Platform: Equatable, CaseIterable { + case amazon + case bigCartel + case bigCommerce + case eBay + case etsy + case facebookMarketplace + case googleShopping + case pinterest + case shopify + case square + case squarespace + case wix + case wordPress + } + + let topHeader: String + + let title: String = Localization.title + + let subtitle: String = Localization.subtitle + + /// Question content. + /// TODO: 8376 - update values when API is ready. + let platforms: [Platform] = Platform.allCases + + @Published private(set) var selectedPlatforms: Set = [] + + private let onContinue: () -> Void + private let onSkip: () -> Void + + init(storeName: String, + onContinue: @escaping () -> Void, + onSkip: @escaping () -> Void) { + self.topHeader = storeName + self.onContinue = onContinue + self.onSkip = onSkip + } +} + +extension StoreCreationSellingPlatformsQuestionViewModel: OptionalStoreCreationProfilerQuestionViewModel { + func continueButtonTapped() async { + // TODO: submission API. + onContinue() + } + + func skipButtonTapped() { + onSkip() + } +} + +extension StoreCreationSellingPlatformsQuestionViewModel { + /// Called when a platform is selected. + func selectPlatform(_ platform: Platform) { + if selectedPlatforms.contains(platform) { + selectedPlatforms.remove(platform) + } else { + selectedPlatforms.insert(platform) + } + } +} + +extension StoreCreationSellingPlatformsQuestionViewModel.Platform { + var description: String { + switch self { + case .amazon: + return NSLocalizedString( + "Amazon", + comment: "Option in the store creation selling platforms question." + ) + case .bigCartel: + return NSLocalizedString( + "Big Cartel", + comment: "Option in the store creation selling platforms question." + ) + case .bigCommerce: + return NSLocalizedString( + "Big Commerce", + comment: "Option in the store creation selling platforms question." + ) + case .eBay: + return NSLocalizedString( + "Ebay", + comment: "Option in the store creation selling platforms question." + ) + case .etsy: + return NSLocalizedString( + "Etsy", + comment: "Option in the store creation selling platforms question." + ) + case .facebookMarketplace: + return NSLocalizedString( + "Facebook Marketplace", + comment: "Option in the store creation selling platforms question." + ) + case .googleShopping: + return NSLocalizedString( + "Google Shopping", + comment: "Option in the store creation selling platforms question." + ) + case .pinterest: + return NSLocalizedString( + "Pinterest", + comment: "Option in the store creation selling platforms question." + ) + case .shopify: + return NSLocalizedString( + "Shopify", + comment: "Option in the store creation selling platforms question." + ) + case .square: + return NSLocalizedString( + "Square", + comment: "Option in the store creation selling platforms question." + ) + case .squarespace: + return NSLocalizedString( + "Squarespace", + comment: "Option in the store creation selling platforms question." + ) + case .wix: + return NSLocalizedString( + "Wix", + comment: "Option in the store creation selling platforms question." + ) + case .wordPress: + return NSLocalizedString( + "WordPress", + comment: "Option in the store creation selling platforms question." + ) + } + } +} + +private extension StoreCreationSellingPlatformsQuestionViewModel { + enum Localization { + static let title = NSLocalizedString( + "In which platform are you currently selling?", + comment: "Title of the store creation profiler question about the store selling platforms." + ) + static let subtitle = NSLocalizedString( + "You can choose multiple ones.", + comment: "Subtitle of the store creation profiler question about the store selling platforms." + ) + } +} diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionContainerView.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionContainerView.swift new file mode 100644 index 00000000000..2c59dadec84 --- /dev/null +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionContainerView.swift @@ -0,0 +1,51 @@ +import SwiftUI + +/// Hosting controller that wraps the `StoreCreationSellingStatusQuestionContainerView`. +final class StoreCreationSellingStatusQuestionHostingController: UIHostingController { + init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) { + super.init(rootView: StoreCreationSellingStatusQuestionContainerView(storeName: storeName, onContinue: onContinue, onSkip: onSkip)) + } + + @available(*, unavailable) + required dynamic init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + configureTransparentNavigationBar() + } +} + +/// Displays the selling status question initially. If the user chooses the "I'm already selling online" option, the selling +/// platforms question is shown. +struct StoreCreationSellingStatusQuestionContainerView: View { + @StateObject private var viewModel: StoreCreationSellingStatusQuestionViewModel + private let storeName: String + private let onContinue: () -> Void + private let onSkip: () -> Void + + init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) { + self._viewModel = StateObject(wrappedValue: StoreCreationSellingStatusQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip)) + self.storeName = storeName + self.onContinue = onContinue + self.onSkip = onSkip + } + + var body: some View { + if viewModel.isAlreadySellingOnline { + StoreCreationSellingPlatformsQuestionView(storeName: storeName, onContinue: onContinue, onSkip: onSkip) + } else { + StoreCreationSellingStatusQuestionView(viewModel: viewModel) + } + } +} + +struct StoreCreationSellingStatusQuestionContainerView_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + StoreCreationSellingStatusQuestionContainerView(storeName: "New Year Store", onContinue: {}, onSkip: {}) + } + } +} diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionView.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionView.swift index aeba671401f..b7636e7aa6e 100644 --- a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionView.swift +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionView.swift @@ -1,29 +1,11 @@ import SwiftUI -/// Hosting controller that wraps the `StoreCreationSellingStatusQuestionView`. -final class StoreCreationSellingStatusQuestionHostingController: UIHostingController { - init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) { - super.init(rootView: StoreCreationSellingStatusQuestionView(storeName: storeName, onContinue: onContinue, onSkip: onSkip)) - } - - @available(*, unavailable) - required dynamic init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - configureTransparentNavigationBar() - } -} - /// Shows the store selling status question in the store creation flow. struct StoreCreationSellingStatusQuestionView: View { @ObservedObject private var viewModel: StoreCreationSellingStatusQuestionViewModel - init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) { - self.viewModel = StoreCreationSellingStatusQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip) + init(viewModel: StoreCreationSellingStatusQuestionViewModel) { + self.viewModel = viewModel } var body: some View { @@ -47,6 +29,8 @@ struct StoreCreationSellingStatusQuestionView: View { struct StoreCreationSellingStatusQuestionView_Previews: PreviewProvider { static var previews: some View { - StoreCreationSellingStatusQuestionView(storeName: "New Year Store", onContinue: {}, onSkip: {}) + NavigationView { + StoreCreationSellingStatusQuestionView(viewModel: .init(storeName: "New Year Store", onContinue: {}, onSkip: {})) + } } } diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionViewModel.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionViewModel.swift index 6e6fd4facda..4be9314539e 100644 --- a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionViewModel.swift +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionViewModel.swift @@ -27,6 +27,9 @@ final class StoreCreationSellingStatusQuestionViewModel: StoreCreationProfilerQu @Published private(set) var selectedStatus: SellingStatus? + /// Set to `true` when the user selects the selling status as "I am already selling online". + @Published private(set) var isAlreadySellingOnline: Bool = false + private let onContinue: () -> Void private let onSkip: () -> Void @@ -36,6 +39,10 @@ final class StoreCreationSellingStatusQuestionViewModel: StoreCreationProfilerQu self.topHeader = storeName self.onContinue = onContinue self.onSkip = onSkip + + $selectedStatus + .map { $0 == .alreadySellingOnline } + .assign(to: &$isAlreadySellingOnline) } } @@ -44,6 +51,11 @@ extension StoreCreationSellingStatusQuestionViewModel: OptionalStoreCreationProf guard selectedStatus != nil else { return onSkip() } + guard selectedStatus != .alreadySellingOnline else { + // Handled in `StoreCreationSellingPlatformsQuestionViewModel`. + return + } + // TODO: submission API. onContinue() } diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index c5f28d68a95..dda5e684b9f 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -251,6 +251,7 @@ 026B3C57249A046E00F7823C /* TextFieldTextAlignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026B3C56249A046E00F7823C /* TextFieldTextAlignment.swift */; }; 026CF63A237E9ABE009563D4 /* ProductVariationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026CF638237E9ABE009563D4 /* ProductVariationsViewController.swift */; }; 026CF63B237E9ABE009563D4 /* ProductVariationsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 026CF639237E9ABE009563D4 /* ProductVariationsViewController.xib */; }; + 026D4650295C08CA0037F59A /* StoreCreationSellingPlatformsQuestionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026D464F295C08CA0037F59A /* StoreCreationSellingPlatformsQuestionViewModelTests.swift */; }; 026D4A24280461960090164F /* LegacyCollectOrderPaymentUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026D4A23280461960090164F /* LegacyCollectOrderPaymentUseCaseTests.swift */; }; 0270F47624D005B00005210A /* ProductFormViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0270F47524D005B00005210A /* ProductFormViewModelProtocol.swift */; }; 0270F47824D006F60005210A /* ProductFormPresentationStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0270F47724D006F60005210A /* ProductFormPresentationStyle.swift */; }; @@ -297,7 +298,10 @@ 0286B27C23C7051F003D784B /* ProductImagesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0286B27823C7051F003D784B /* ProductImagesViewController.xib */; }; 0286B27D23C7051F003D784B /* ProductImagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0286B27923C7051F003D784B /* ProductImagesViewController.swift */; }; 0286B27F23C70557003D784B /* ColumnFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0286B27E23C70557003D784B /* ColumnFlowLayout.swift */; }; + 028A465329597A91001CF6CE /* StoreCreationSellingPlatformsQuestionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028A465229597A91001CF6CE /* StoreCreationSellingPlatformsQuestionViewModel.swift */; }; 028A4655295AD2DA001CF6CE /* StoreCreationSellingStatusQuestionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028A4654295AD2DA001CF6CE /* StoreCreationSellingStatusQuestionViewModelTests.swift */; }; + 028A4657295B2CF4001CF6CE /* StoreCreationSellingPlatformsQuestionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028A4656295B2CF4001CF6CE /* StoreCreationSellingPlatformsQuestionView.swift */; }; + 028A4659295BCFFC001CF6CE /* StoreCreationSellingStatusQuestionContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028A4658295BCFFC001CF6CE /* StoreCreationSellingStatusQuestionContainerView.swift */; }; 028AFFB32484ED2800693C09 /* Dictionary+Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028AFFB22484ED2800693C09 /* Dictionary+Logging.swift */; }; 028AFFB62484EDA000693C09 /* Dictionary+LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028AFFB52484EDA000693C09 /* Dictionary+LoggingTests.swift */; }; 028BAC3D22F2DECE008BB4AF /* StoreStatsAndTopPerformersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028BAC3C22F2DECE008BB4AF /* StoreStatsAndTopPerformersViewController.swift */; }; @@ -2290,6 +2294,7 @@ 026B3C56249A046E00F7823C /* TextFieldTextAlignment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldTextAlignment.swift; sourceTree = ""; }; 026CF638237E9ABE009563D4 /* ProductVariationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductVariationsViewController.swift; sourceTree = ""; }; 026CF639237E9ABE009563D4 /* ProductVariationsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProductVariationsViewController.xib; sourceTree = ""; }; + 026D464F295C08CA0037F59A /* StoreCreationSellingPlatformsQuestionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationSellingPlatformsQuestionViewModelTests.swift; sourceTree = ""; }; 026D4A23280461960090164F /* LegacyCollectOrderPaymentUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyCollectOrderPaymentUseCaseTests.swift; sourceTree = ""; }; 0270C0A827069BEF00FC799F /* Experiments.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Experiments.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0270F47524D005B00005210A /* ProductFormViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductFormViewModelProtocol.swift; sourceTree = ""; }; @@ -2337,7 +2342,10 @@ 0286B27823C7051F003D784B /* ProductImagesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ProductImagesViewController.xib; sourceTree = ""; }; 0286B27923C7051F003D784B /* ProductImagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductImagesViewController.swift; sourceTree = ""; }; 0286B27E23C70557003D784B /* ColumnFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnFlowLayout.swift; sourceTree = ""; }; + 028A465229597A91001CF6CE /* StoreCreationSellingPlatformsQuestionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationSellingPlatformsQuestionViewModel.swift; sourceTree = ""; }; 028A4654295AD2DA001CF6CE /* StoreCreationSellingStatusQuestionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationSellingStatusQuestionViewModelTests.swift; sourceTree = ""; }; + 028A4656295B2CF4001CF6CE /* StoreCreationSellingPlatformsQuestionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationSellingPlatformsQuestionView.swift; sourceTree = ""; }; + 028A4658295BCFFC001CF6CE /* StoreCreationSellingStatusQuestionContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationSellingStatusQuestionContainerView.swift; sourceTree = ""; }; 028AFFB22484ED2800693C09 /* Dictionary+Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Logging.swift"; sourceTree = ""; }; 028AFFB52484EDA000693C09 /* Dictionary+LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+LoggingTests.swift"; sourceTree = ""; }; 028BAC3C22F2DECE008BB4AF /* StoreStatsAndTopPerformersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreStatsAndTopPerformersViewController.swift; sourceTree = ""; }; @@ -4141,6 +4149,7 @@ children = ( 0201E4302946FFDB00C793C7 /* StoreCreationCategoryQuestionViewModelTests.swift */, 028A4654295AD2DA001CF6CE /* StoreCreationSellingStatusQuestionViewModelTests.swift */, + 026D464F295C08CA0037F59A /* StoreCreationSellingPlatformsQuestionViewModelTests.swift */, ); path = Profiler; sourceTree = ""; @@ -4265,6 +4274,9 @@ children = ( 020DD0AE294A06C400727BEF /* StoreCreationSellingStatusQuestionView.swift */, 020DD0B0294A071600727BEF /* StoreCreationSellingStatusQuestionViewModel.swift */, + 028A465229597A91001CF6CE /* StoreCreationSellingPlatformsQuestionViewModel.swift */, + 028A4656295B2CF4001CF6CE /* StoreCreationSellingPlatformsQuestionView.swift */, + 028A4658295BCFFC001CF6CE /* StoreCreationSellingStatusQuestionContainerView.swift */, ); path = "Selling Status"; sourceTree = ""; @@ -10385,6 +10397,7 @@ B5A56BF0219F2CE90065A902 /* VerticalButton.swift in Sources */, D831E2DC230E0558000037D0 /* Authentication.swift in Sources */, 26DB7E3528636D2200506173 /* NonEditableOrderBanner.swift in Sources */, + 028A4657295B2CF4001CF6CE /* StoreCreationSellingPlatformsQuestionView.swift in Sources */, 314DC4BF268D183600444C9E /* CardReaderSettingsKnownReaderStorage.swift in Sources */, 2662D90826E15D6E00E25611 /* AreaSelectorCommand.swift in Sources */, 02393069291A065000B2632F /* DomainRowView.swift in Sources */, @@ -10763,6 +10776,7 @@ 31B0551E264B3C7A00134D87 /* CardPresentModalFoundReader.swift in Sources */, 4520A15E2722BA3E001FA573 /* OrderDateRangeFilter+Utils.swift in Sources */, DEE183F1292E0ED0008818AB /* LoginJetpackSetupInterruptedView.swift in Sources */, + 028A465329597A91001CF6CE /* StoreCreationSellingPlatformsQuestionViewModel.swift in Sources */, 2676F4CC2908284800C7A15B /* ProductCreationTypeCommand.swift in Sources */, 45A24E5F2451DF1A0050606B /* ProductMenuOrderViewController.swift in Sources */, 0201E4272945B01800C793C7 /* StoreCreationProfilerQuestionView.swift in Sources */, @@ -10814,6 +10828,7 @@ DE8C946E264699B600C94823 /* PluginListViewModel.swift in Sources */, 021125992578D9C20075AD2A /* ShippingLabelPrintingInstructionsView.swift in Sources */, 03E471D42942096B001A58AD /* BuiltInCardReaderPaymentAlertsProvider.swift in Sources */, + 028A4659295BCFFC001CF6CE /* StoreCreationSellingStatusQuestionContainerView.swift in Sources */, 68E952CC287536010095A23D /* SafariView.swift in Sources */, D449C51C26DE6B5000D75B02 /* IconListItem.swift in Sources */, CE16177A21B7192A00B82A47 /* AuthenticationConstants.swift in Sources */, @@ -11569,6 +11584,7 @@ 09C6A26227C01166001FAD73 /* BulkUpdateViewModelTests.swift in Sources */, 6856D806DE7DB61522D54044 /* NSMutableAttributedStringHelperTests.swift in Sources */, 023D69442588C6BD00F7DA72 /* ShippingLabelPaperSizeListSelectorCommandTests.swift in Sources */, + 026D4650295C08CA0037F59A /* StoreCreationSellingPlatformsQuestionViewModelTests.swift in Sources */, 6856DF20E1BDCC391635F707 /* AgeTests.swift in Sources */, 025A1248247CE793008EA761 /* ProductFormViewModel+ObservablesTests.swift in Sources */, BAFEF51E273C2151005F94CC /* SettingsViewModelTests.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationSellingPlatformsQuestionViewModelTests.swift b/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationSellingPlatformsQuestionViewModelTests.swift new file mode 100644 index 00000000000..74abec4fd00 --- /dev/null +++ b/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationSellingPlatformsQuestionViewModelTests.swift @@ -0,0 +1,91 @@ +import XCTest +@testable import WooCommerce + +@MainActor +final class StoreCreationSellingPlatformsQuestionViewModelTests: XCTestCase { + func test_topHeader_is_set_to_store_name() throws { + // Given + let viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: "store 🌟") {} onSkip: {} + + // Then + XCTAssertEqual(viewModel.topHeader, "store 🌟") + } + + func test_selecting_a_platform_adds_to_selectedPlatforms() throws { + // Given + let viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: "store") {} onSkip: {} + XCTAssertEqual(viewModel.selectedPlatforms, []) + + // When + viewModel.selectPlatform(.wordPress) + + // Then + XCTAssertEqual(viewModel.selectedPlatforms, [.wordPress]) + + // When + viewModel.selectPlatform(.amazon) + + // Then + XCTAssertEqual(viewModel.selectedPlatforms, [.wordPress, .amazon]) + } + + func test_selecting_a_platform_twice_removes_platform_from_selectedPlatforms() throws { + // Given + let viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: "store") {} onSkip: {} + XCTAssertEqual(viewModel.selectedPlatforms, []) + + // When + viewModel.selectPlatform(.wordPress) + + // Then + XCTAssertEqual(viewModel.selectedPlatforms, [.wordPress]) + + // When + viewModel.selectPlatform(.wordPress) + + // Then + XCTAssertEqual(viewModel.selectedPlatforms, []) + } + + func test_continueButtonTapped_invokes_onContinue_after_selecting_a_platform() throws { + waitFor { promise in + // Given + let viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: "store") { + // Then + promise(()) + } onSkip: {} + + // When + viewModel.selectPlatform(.wordPress) + Task { @MainActor in + await viewModel.continueButtonTapped() + } + } + } + + func test_continueButtonTapped_invokes_onContinue_without_selecting_a_category() throws { + waitFor { promise in + // Given + let viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: "store") { + // Then + promise(()) + } onSkip: {} + // When + Task { @MainActor in + await viewModel.continueButtonTapped() + } + } + } + + func test_skipButtonTapped_invokes_onSkip() throws { + waitFor { promise in + // Given + let viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: "store") {} onSkip: { + // Then + promise(()) + } + // When + viewModel.skipButtonTapped() + } + } +} diff --git a/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationSellingStatusQuestionViewModelTests.swift b/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationSellingStatusQuestionViewModelTests.swift index 0e96f4a0e4b..8c3090ca349 100644 --- a/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationSellingStatusQuestionViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationSellingStatusQuestionViewModelTests.swift @@ -3,18 +3,33 @@ import XCTest @MainActor final class StoreCreationSellingStatusQuestionViewModelTests: XCTestCase { - func test_selectCategory_updates_selectedStatus() throws { + func test_selecting_non_alreadySellingOnline_updates_selectedStatus_and_not_isAlreadySellingOnline() throws { // Given let viewModel = StoreCreationSellingStatusQuestionViewModel(storeName: "store") {} onSkip: {} + XCTAssertFalse(viewModel.isAlreadySellingOnline) + + // When + viewModel.selectStatus(.alreadySellingButNotOnline) + + // Then + XCTAssertEqual(viewModel.selectedStatus, .alreadySellingButNotOnline) + XCTAssertFalse(viewModel.isAlreadySellingOnline) + } + + func test_selecting_alreadySellingOnline_updates_selectedStatus_and_isAlreadySellingOnline() throws { + // Given + let viewModel = StoreCreationSellingStatusQuestionViewModel(storeName: "store") {} onSkip: {} + XCTAssertFalse(viewModel.isAlreadySellingOnline) // When viewModel.selectStatus(.alreadySellingOnline) // Then XCTAssertEqual(viewModel.selectedStatus, .alreadySellingOnline) + XCTAssertTrue(viewModel.isAlreadySellingOnline) } - func test_continueButtonTapped_invokes_onContinue_after_selecting_a_status() throws { + func test_continueButtonTapped_invokes_onContinue_after_selecting_a_non_alreadySellingOnline_status() throws { waitFor { promise in // Given let viewModel = StoreCreationSellingStatusQuestionViewModel(storeName: "store") { @@ -29,6 +44,19 @@ final class StoreCreationSellingStatusQuestionViewModelTests: XCTestCase { } } + func test_continueButtonTapped_does_not_invoke_onContinue_after_selecting_alreadySellingOnline_status() throws { + // Given + let viewModel = StoreCreationSellingStatusQuestionViewModel(storeName: "store") { + XCTFail("onContinue should not be invoked after selecting alreadySellingOnline status.") + } onSkip: {} + + // When + viewModel.selectStatus(.alreadySellingOnline) + Task { @MainActor in + await viewModel.continueButtonTapped() + } + } + func test_continueButtonTapped_invokes_onSkip_without_selecting_a_category() throws { waitFor { promise in // Given