From 7e019f15e798d3e6450ec59adbb9f73ae090fb74 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 3 Aug 2023 12:07:37 +0530 Subject: [PATCH 1/8] New step value for features screen. --- .../Classes/Analytics/WooAnalyticsEvent+StoreCreation.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsEvent+StoreCreation.swift b/WooCommerce/Classes/Analytics/WooAnalyticsEvent+StoreCreation.swift index 24d47619c0a..f80b480981b 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsEvent+StoreCreation.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsEvent+StoreCreation.swift @@ -191,6 +191,7 @@ extension WooAnalyticsEvent.StoreCreation { case profilerSellingPlatformsQuestion = "store_profiler_ecommerce_platforms" case profilerCountryQuestion = "store_profiler_country" case profilerChallengesQuestion = "store_profiler_challenges" + case profilerFeaturesQuestion = "store_profiler_features" case domainPicker = "domain_picker" case storeSummary = "store_summary" case planPurchase = "plan_purchase" From 9625c7a2a8125d19dc1064e41aa5b82cdaa2a246 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 3 Aug 2023 12:08:25 +0530 Subject: [PATCH 2/8] View model for store creation profiler features question screen. --- ...StoreCreationFeaturesQuestionOptions.swift | 42 +++++++++++ ...oreCreationFeaturesQuestionViewModel.swift | 74 +++++++++++++++++++ .../WooCommerce.xcodeproj/project.pbxproj | 24 ++++++ 3 files changed, 140 insertions(+) create mode 100644 WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift create mode 100644 WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionViewModel.swift diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift new file mode 100644 index 00000000000..39ecc5d52e7 --- /dev/null +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift @@ -0,0 +1,42 @@ +import Foundation + +extension StoreCreationFeaturesQuestionViewModel { + // TODO: 10386 Align with Android and send these via tracks + enum Feature: String, CaseIterable { + case salesAndAnalyticsReports + case productManagementAndInventoryTracking + case flexibleAndSecurePaymentOptions + case inPersonPayment + case abilityToScaleAsBusinessGrows + case customisationOptionForStoreDesign + case wideRangeOfPluginsAndExtensions + case others + } + + var features: [Feature] { + Feature.allCases + } +} + +extension StoreCreationFeaturesQuestionViewModel.Feature { + var name: String { + switch self { + case .salesAndAnalyticsReports: + return NSLocalizedString("Comprehensive sales and analytics reports", comment: "Feature option in the store creation features question.") + case .productManagementAndInventoryTracking: + return NSLocalizedString("Easy product management and inventory tracking", comment: "Feature option in the store creation features question.") + case .flexibleAndSecurePaymentOptions: + return NSLocalizedString("Flexible and secure payment options", comment: "Feature option in the store creation features question.") + case .inPersonPayment: + return NSLocalizedString("In-person payment", comment: "Feature option in the store creation features question.") + case .abilityToScaleAsBusinessGrows: + return NSLocalizedString("Ability to scale as my business grows", comment: "Feature option in the store creation features question.") + case .customisationOptionForStoreDesign: + return NSLocalizedString("Customization options for my store design", comment: "Feature option in the store creation features question.") + case .wideRangeOfPluginsAndExtensions: + return NSLocalizedString("Access to a wide range of plugins and extensions", comment: "Feature option in the store creation features question.") + case .others: + return NSLocalizedString("Others", comment: "Feature option in the store creation features question.") + } + } +} diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionViewModel.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionViewModel.swift new file mode 100644 index 00000000000..f027ab9e432 --- /dev/null +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionViewModel.swift @@ -0,0 +1,74 @@ +import Combine +import Foundation + +/// Necessary data from the answer of the store creation features question. +struct StoreCreationFeaturesAnswer: Equatable { + /// Display name of the selected feature. + let name: String + /// Raw value of the feature to be sent to the backend. + let value: String +} + +/// View model for `StoreCreationFeaturesQuestionView`, an optional profiler question about features in the store creation flow. +@MainActor +final class StoreCreationFeaturesQuestionViewModel: StoreCreationProfilerQuestionViewModel, ObservableObject { + typealias Answer = StoreCreationFeaturesAnswer + + let topHeader = Localization.topHeader + + let title = Localization.title + + let subtitle = Localization.subtitle + + @Published private(set) var selectedFeatures: [Feature] = [] + + private let onContinue: ([Answer]) -> Void + private let onSkip: () -> Void + + init(onContinue: @escaping ([Answer]) -> Void, + onSkip: @escaping () -> Void) { + self.onContinue = onContinue + self.onSkip = onSkip + } +} + +extension StoreCreationFeaturesQuestionViewModel: OptionalStoreCreationProfilerQuestionViewModel { + func continueButtonTapped() async { + guard selectedFeatures.isNotEmpty else { + return onSkip() + } + + onContinue(selectedFeatures.map { .init(name: $0.name, value: $0.rawValue) }) + } + + func skipButtonTapped() { + onSkip() + } +} + +extension StoreCreationFeaturesQuestionViewModel { + func didTapFeature(_ feature: Feature) { + if let alreadySelectedIndex = selectedFeatures.firstIndex(of: feature) { + selectedFeatures.remove(at: alreadySelectedIndex) + } else { + selectedFeatures.append(feature) + } + } +} + +private extension StoreCreationFeaturesQuestionViewModel { + enum Localization { + static let topHeader = NSLocalizedString( + "About you", + comment: "Top header text of the store creation profiler question about the features." + ) + static let title = NSLocalizedString( + "Which features are you most interested in?", + comment: "Title of the store creation profiler question about the features." + ) + static let subtitle = NSLocalizedString( + "Let us know what you are looking forward to using in our app.", + comment: "Subtitle of the store creation profiler question about the features." + ) + } +} diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 2ff066065c0..51a65e57fac 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -2277,6 +2277,10 @@ EE57C11F297E742200BC31E7 /* WooAnalyticsEvent+ApplicationPassword.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE57C11E297E742200BC31E7 /* WooAnalyticsEvent+ApplicationPassword.swift */; }; EE57C121297E76E000BC31E7 /* TrackEventRequestNotificationHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE57C120297E76E000BC31E7 /* TrackEventRequestNotificationHandlerTests.swift */; }; EE5A0A1C2A6908A800DA5926 /* WooAnalyticsEvent+LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE5A0A1B2A6908A800DA5926 /* WooAnalyticsEvent+LocalNotification.swift */; }; + EE6A7BA92A7B7BE600D9A028 /* StoreCreationFeaturesQuestionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6A7BA82A7B7BE600D9A028 /* StoreCreationFeaturesQuestionViewModel.swift */; }; + EE6A7BAB2A7B7C0100D9A028 /* StoreCreationFeaturesQuestionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6A7BAA2A7B7C0100D9A028 /* StoreCreationFeaturesQuestionView.swift */; }; + EE6A7BAD2A7B7C1D00D9A028 /* StoreCreationFeaturesQuestionOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6A7BAC2A7B7C1D00D9A028 /* StoreCreationFeaturesQuestionOptions.swift */; }; + EE6A7BAF2A7B811700D9A028 /* StoreCreationFeaturesQuestionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6A7BAE2A7B811700D9A028 /* StoreCreationFeaturesQuestionViewModelTests.swift */; }; EE6B2AD129DC522300048A8F /* StoreCreationProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6B2AD029DC522300048A8F /* StoreCreationProgressView.swift */; }; EE6B2AD329DD285A00048A8F /* StoreCreationProgressViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6B2AD229DD285900048A8F /* StoreCreationProgressViewModel.swift */; }; EE6F08662A718DFB00AA9B88 /* FreeTrialSurveyViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6F08652A718DFB00AA9B88 /* FreeTrialSurveyViewModelTests.swift */; }; @@ -4703,6 +4707,10 @@ EE57C11E297E742200BC31E7 /* WooAnalyticsEvent+ApplicationPassword.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WooAnalyticsEvent+ApplicationPassword.swift"; sourceTree = ""; }; EE57C120297E76E000BC31E7 /* TrackEventRequestNotificationHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackEventRequestNotificationHandlerTests.swift; sourceTree = ""; }; EE5A0A1B2A6908A800DA5926 /* WooAnalyticsEvent+LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WooAnalyticsEvent+LocalNotification.swift"; sourceTree = ""; }; + EE6A7BA82A7B7BE600D9A028 /* StoreCreationFeaturesQuestionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationFeaturesQuestionViewModel.swift; sourceTree = ""; }; + EE6A7BAA2A7B7C0100D9A028 /* StoreCreationFeaturesQuestionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationFeaturesQuestionView.swift; sourceTree = ""; }; + EE6A7BAC2A7B7C1D00D9A028 /* StoreCreationFeaturesQuestionOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationFeaturesQuestionOptions.swift; sourceTree = ""; }; + EE6A7BAE2A7B811700D9A028 /* StoreCreationFeaturesQuestionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationFeaturesQuestionViewModelTests.swift; sourceTree = ""; }; EE6B2AD029DC522300048A8F /* StoreCreationProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationProgressView.swift; sourceTree = ""; }; EE6B2AD229DD285900048A8F /* StoreCreationProgressViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCreationProgressViewModel.swift; sourceTree = ""; }; EE6F08652A718DFB00AA9B88 /* FreeTrialSurveyViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FreeTrialSurveyViewModelTests.swift; sourceTree = ""; }; @@ -4873,6 +4881,7 @@ 0201E42E2946F9F400C793C7 /* Category */, 026D4655295D7A380037F59A /* Country */, EED028622A7AB4C800C5DE03 /* Challenges */, + EE6A7BA72A7B7BC900D9A028 /* Features */, ); path = Profiler; sourceTree = ""; @@ -4895,6 +4904,7 @@ 026D464F295C08CA0037F59A /* StoreCreationSellingPlatformsQuestionViewModelTests.swift */, 022F2FA9295E8241003A0A46 /* StoreCreationCountryQuestionViewModelTests.swift */, EED028692A7B640300C5DE03 /* StoreCreationChallengesQuestionViewModelTests.swift */, + EE6A7BAE2A7B811700D9A028 /* StoreCreationFeaturesQuestionViewModelTests.swift */, ); path = Profiler; sourceTree = ""; @@ -10638,6 +10648,16 @@ path = StoreDetails; sourceTree = ""; }; + EE6A7BA72A7B7BC900D9A028 /* Features */ = { + isa = PBXGroup; + children = ( + EE6A7BA82A7B7BE600D9A028 /* StoreCreationFeaturesQuestionViewModel.swift */, + EE6A7BAA2A7B7C0100D9A028 /* StoreCreationFeaturesQuestionView.swift */, + EE6A7BAC2A7B7C1D00D9A028 /* StoreCreationFeaturesQuestionOptions.swift */, + ); + path = Features; + sourceTree = ""; + }; EE6B2ACD29DC488700048A8F /* Progress */ = { isa = PBXGroup; children = ( @@ -11981,6 +12001,7 @@ 45EF7984244F26BB00B22BA2 /* Array+IndexPath.swift in Sources */, 02E6B97823853D81000A36F0 /* TitleAndValueTableViewCell.swift in Sources */, AE2E5F6629685CF8009262D3 /* ProductsListViewModel.swift in Sources */, + EE6A7BAB2A7B7C0100D9A028 /* StoreCreationFeaturesQuestionView.swift in Sources */, CC770C8A27B1497700CE6ABC /* SearchHeader.swift in Sources */, 02BAB02724D13A6400F8B06E /* ProductVariationFormActionsFactory.swift in Sources */, 45CDAFAE2434CFCA00F83C22 /* ProductCatalogVisibilityViewController.swift in Sources */, @@ -12462,6 +12483,7 @@ D81D9228222E7F0800FFA585 /* OrderStatusListViewController.swift in Sources */, CEE006082077D14C0079161F /* OrderDetailsViewController.swift in Sources */, AEB73C0C25CD734200A8454A /* AttributePickerViewModel.swift in Sources */, + EE6A7BAD2A7B7C1D00D9A028 /* StoreCreationFeaturesQuestionOptions.swift in Sources */, D8752EF7265E60F4008ACC80 /* PaymentCaptureCelebration.swift in Sources */, EE6B2AD129DC522300048A8F /* StoreCreationProgressView.swift in Sources */, B58B4AB62108F11C00076FDD /* Notice.swift in Sources */, @@ -12791,6 +12813,7 @@ 0396CFAD2981476900E91436 /* CardPresentModalBuiltInConnectingFailed.swift in Sources */, 02C1853B27FF0D9C00ABD764 /* RefundSubmissionUseCase.swift in Sources */, 26C98F9B29C18ACE00F96503 /* StorePlanBanner.swift in Sources */, + EE6A7BA92A7B7BE600D9A028 /* StoreCreationFeaturesQuestionViewModel.swift in Sources */, E10BD16D27CF890800CE6449 /* InPersonPaymentsCountryNotSupportedStripe.swift in Sources */, 68E674AB2A4DAB8C0034BA1E /* CompletedUpgradeView.swift in Sources */, 26F94E26267A559300DB6CCF /* ProductAddOn.swift in Sources */, @@ -13406,6 +13429,7 @@ 262AF38A2713B67600E39AFF /* SimplePaymentsAmountViewModelTests.swift in Sources */, 93FA787221CD2A1A00B663E5 /* CurrencySettingsTests.swift in Sources */, 45FBDF2D238BF8BF00127F77 /* AddProductImageCollectionViewCellTests.swift in Sources */, + EE6A7BAF2A7B811700D9A028 /* StoreCreationFeaturesQuestionViewModelTests.swift in Sources */, 578195FC25AD1D7C004A5C12 /* OrderFulfillmentUseCaseTests.swift in Sources */, 094C161227B0604700B25F51 /* ProductVariationFormViewModelTests.swift in Sources */, EEAA45FD293073FE0047D125 /* JetpackInstallStepTests.swift in Sources */, From 73939834a75e26bfbef5102257206d38c0be12ec Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 3 Aug 2023 12:08:54 +0530 Subject: [PATCH 3/8] UI view for store creation profiler features question screen. --- .../StoreCreationFeaturesQuestionView.swift | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionView.swift diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionView.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionView.swift new file mode 100644 index 00000000000..e8b9ff2a1fc --- /dev/null +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionView.swift @@ -0,0 +1,53 @@ +import SwiftUI + +/// Hosting controller that wraps the `StoreCreationFeaturesQuestionView`. +final class StoreCreationFeaturesQuestionHostingController: UIHostingController { + init(viewModel: StoreCreationFeaturesQuestionViewModel) { + super.init(rootView: StoreCreationFeaturesQuestionView(viewModel: viewModel)) + } + + @available(*, unavailable) + required dynamic init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + configureTransparentNavigationBar() + } +} + +/// Shows the store features question in the store creation flow. +struct StoreCreationFeaturesQuestionView: View { + @ObservedObject private var viewModel: StoreCreationFeaturesQuestionViewModel + + init(viewModel: StoreCreationFeaturesQuestionViewModel) { + self.viewModel = viewModel + } + + var body: some View { + OptionalStoreCreationProfilerQuestionView(viewModel: viewModel) { + VStack(alignment: .leading, spacing: 16) { + ForEach(viewModel.features, id: \.self) { feature in + Button(action: { + viewModel.didTapFeature(feature) + }, label: { + HStack { + Text(feature.name) + Spacer() + } + }) + .buttonStyle(SelectableSecondaryButtonStyle(isSelected: viewModel.selectedFeatures.contains(where: { $0 == feature }))) + } + } + } + } +} + +struct StoreCreationFeaturesQuestionView_Previews: PreviewProvider { + static var previews: some View { + StoreCreationFeaturesQuestionView(viewModel: .init(onContinue: { _ in }, + onSkip: {})) + } +} From d805296ee9fe0b87fc60d0b0f2a3275ff1390170 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 3 Aug 2023 12:19:19 +0530 Subject: [PATCH 4/8] Add raw values for Feature enum cases. --- .../StoreCreationFeaturesQuestionOptions.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift index 39ecc5d52e7..2832013559e 100644 --- a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift @@ -3,14 +3,14 @@ import Foundation extension StoreCreationFeaturesQuestionViewModel { // TODO: 10386 Align with Android and send these via tracks enum Feature: String, CaseIterable { - case salesAndAnalyticsReports - case productManagementAndInventoryTracking - case flexibleAndSecurePaymentOptions - case inPersonPayment - case abilityToScaleAsBusinessGrows - case customisationOptionForStoreDesign - case wideRangeOfPluginsAndExtensions - case others + case salesAndAnalyticsReports = "sales-and-analytics-reports" + case productManagementAndInventoryTracking = "product-management-and-inventory-tracking" + case flexibleAndSecurePaymentOptions = "flexible-and-secure-payment-options" + case inPersonPayment = "in-person-payment" + case abilityToScaleAsBusinessGrows = "ability-to-scale-as-business-grows" + case customisationOptionForStoreDesign = "customization-options-for-my-store-design" + case wideRangeOfPluginsAndExtensions = "wide-range-of-plugins-and-extensions" + case others = "cthers" } var features: [Feature] { From 83eb0748c665b10b70e0c2fabdd82137dc8d6929 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 3 Aug 2023 12:19:53 +0530 Subject: [PATCH 5/8] Unit tests for features question view model. --- ...eationFeaturesQuestionViewModelTests.swift | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationFeaturesQuestionViewModelTests.swift diff --git a/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationFeaturesQuestionViewModelTests.swift b/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationFeaturesQuestionViewModelTests.swift new file mode 100644 index 00000000000..d7fdc60e4ef --- /dev/null +++ b/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationFeaturesQuestionViewModelTests.swift @@ -0,0 +1,110 @@ +import XCTest +@testable import WooCommerce + +@MainActor +final class StoreCreationFeaturesQuestionViewModelTests: XCTestCase { + func test_didTapFeature_adds_feature_to_selectedFeatures() throws { + // Given + let viewModel = StoreCreationFeaturesQuestionViewModel(onContinue: { _ in }, + onSkip: {}) + + // When + viewModel.didTapFeature(.productManagementAndInventoryTracking) + viewModel.didTapFeature(.abilityToScaleAsBusinessGrows) + + // Then + XCTAssertEqual(viewModel.selectedFeatures, [.productManagementAndInventoryTracking, .abilityToScaleAsBusinessGrows]) + } + + func test_didTapFeature_removes_feature_from_selectedFeatures_if_already_selected() throws { + // Given + let viewModel = StoreCreationFeaturesQuestionViewModel(onContinue: { _ in }, + onSkip: {}) + + // When + viewModel.didTapFeature(.productManagementAndInventoryTracking) + viewModel.didTapFeature(.abilityToScaleAsBusinessGrows) + + // Then + XCTAssertEqual(viewModel.selectedFeatures, [.productManagementAndInventoryTracking, .abilityToScaleAsBusinessGrows]) + + // When + viewModel.didTapFeature(.productManagementAndInventoryTracking) + + // Then + XCTAssertEqual(viewModel.selectedFeatures, [.abilityToScaleAsBusinessGrows]) + } + + func test_continueButtonTapped_invokes_onContinue_after_selecting_features() throws { + let answer = waitFor { promise in + // Given + let viewModel = StoreCreationFeaturesQuestionViewModel(onContinue: { answer in + promise(answer) + }, + onSkip: {}) + // When + viewModel.didTapFeature(.productManagementAndInventoryTracking) + viewModel.didTapFeature(.abilityToScaleAsBusinessGrows) + + Task { @MainActor in + await viewModel.continueButtonTapped() + } + } + + // Then + XCTAssertEqual(answer, [.init(name: StoreCreationFeaturesQuestionViewModel.Feature.productManagementAndInventoryTracking.name, + value: "product-management-and-inventory-tracking"), + .init(name: StoreCreationFeaturesQuestionViewModel.Feature.abilityToScaleAsBusinessGrows.name, + value: "ability-to-scale-as-business-grows")]) + } + + func test_continueButtonTapped_invokes_onSkip_without_selecting_a_feature() throws { + waitFor { promise in + // Given + let viewModel = StoreCreationFeaturesQuestionViewModel( onContinue: { _ in }, + onSkip: { + // Then + promise(()) + }) + // When + Task { @MainActor in + await viewModel.continueButtonTapped() + } + } + } + + func test_skipButtonTapped_invokes_onSkip() throws { + waitFor { promise in + // Given + let viewModel = StoreCreationFeaturesQuestionViewModel( onContinue: { _ in }, + onSkip: { + // Then + promise(()) + }) + // When + viewModel.skipButtonTapped() + } + } + + func test_features_are_in_the_expected_order() throws { + // Given + let viewModel = StoreCreationFeaturesQuestionViewModel(onContinue: { _ in }, + onSkip: {}) + + // When + let features = viewModel.features + + // Then + XCTAssertEqual(features, + [ + .salesAndAnalyticsReports, + .productManagementAndInventoryTracking, + .flexibleAndSecurePaymentOptions, + .inPersonPayment, + .abilityToScaleAsBusinessGrows, + .customisationOptionForStoreDesign, + .wideRangeOfPluginsAndExtensions, + .others, + ]) + } +} From 35d1500dda3fff292c64263dcc89c94a9eb83807 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 3 Aug 2023 12:21:48 +0530 Subject: [PATCH 6/8] Helper method to show features question screen. --- .../Store Creation/StoreCreationCoordinator.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/WooCommerce/Classes/Authentication/Store Creation/StoreCreationCoordinator.swift b/WooCommerce/Classes/Authentication/Store Creation/StoreCreationCoordinator.swift index 79223363b7a..ad9e30e5715 100644 --- a/WooCommerce/Classes/Authentication/Store Creation/StoreCreationCoordinator.swift +++ b/WooCommerce/Classes/Authentication/Store Creation/StoreCreationCoordinator.swift @@ -257,6 +257,20 @@ private extension StoreCreationCoordinator { analytics.track(event: .StoreCreation.siteCreationStep(step: .profilerChallengesQuestion)) } + @MainActor + func showFeaturesQuestion(from navigationController: UINavigationController) { + let questionController = StoreCreationFeaturesQuestionHostingController(viewModel: + .init { _ in + // TODO: 10376 - Navigate to [progress view / my store tab] and pass the selected features + } onSkip: { [weak self] in + guard let self else { return } + self.analytics.track(event: .StoreCreation.siteCreationProfilerQuestionSkipped(step: .profilerFeaturesQuestion)) + // TODO: 10376 - Navigate to [progress view / my store tab] + }) + navigationController.pushViewController(questionController, animated: true) + analytics.track(event: .StoreCreation.siteCreationStep(step: .profilerFeaturesQuestion)) + } + @MainActor func showCategoryQuestion(from navigationController: UINavigationController, storeName: String) { From 2f8340a4e92b02c2f2143f0272e83333899cc121 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 3 Aug 2023 12:28:31 +0530 Subject: [PATCH 7/8] Stop testing for order as we started using CaseIterable. --- ...eationFeaturesQuestionViewModelTests.swift | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationFeaturesQuestionViewModelTests.swift b/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationFeaturesQuestionViewModelTests.swift index d7fdc60e4ef..f17d9e75896 100644 --- a/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationFeaturesQuestionViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/Authentication/Store Creation/Profiler/StoreCreationFeaturesQuestionViewModelTests.swift @@ -85,26 +85,4 @@ final class StoreCreationFeaturesQuestionViewModelTests: XCTestCase { viewModel.skipButtonTapped() } } - - func test_features_are_in_the_expected_order() throws { - // Given - let viewModel = StoreCreationFeaturesQuestionViewModel(onContinue: { _ in }, - onSkip: {}) - - // When - let features = viewModel.features - - // Then - XCTAssertEqual(features, - [ - .salesAndAnalyticsReports, - .productManagementAndInventoryTracking, - .flexibleAndSecurePaymentOptions, - .inPersonPayment, - .abilityToScaleAsBusinessGrows, - .customisationOptionForStoreDesign, - .wideRangeOfPluginsAndExtensions, - .others, - ]) - } } From f56ec682576ff0a4a4329f7b8011cfc2894cd1c3 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 3 Aug 2023 16:15:52 +0530 Subject: [PATCH 8/8] Fix spelling. --- .../Features/StoreCreationFeaturesQuestionOptions.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift index 2832013559e..388595f5555 100644 --- a/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift +++ b/WooCommerce/Classes/Authentication/Store Creation/Profiler/Features/StoreCreationFeaturesQuestionOptions.swift @@ -8,7 +8,7 @@ extension StoreCreationFeaturesQuestionViewModel { case flexibleAndSecurePaymentOptions = "flexible-and-secure-payment-options" case inPersonPayment = "in-person-payment" case abilityToScaleAsBusinessGrows = "ability-to-scale-as-business-grows" - case customisationOptionForStoreDesign = "customization-options-for-my-store-design" + case customizationOptionForStoreDesign = "customization-options-for-my-store-design" case wideRangeOfPluginsAndExtensions = "wide-range-of-plugins-and-extensions" case others = "cthers" } @@ -31,7 +31,7 @@ extension StoreCreationFeaturesQuestionViewModel.Feature { return NSLocalizedString("In-person payment", comment: "Feature option in the store creation features question.") case .abilityToScaleAsBusinessGrows: return NSLocalizedString("Ability to scale as my business grows", comment: "Feature option in the store creation features question.") - case .customisationOptionForStoreDesign: + case .customizationOptionForStoreDesign: return NSLocalizedString("Customization options for my store design", comment: "Feature option in the store creation features question.") case .wideRangeOfPluginsAndExtensions: return NSLocalizedString("Access to a wide range of plugins and extensions", comment: "Feature option in the store creation features question.")