From ca41149e205735ea3ee87b736c47673310c0864f Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Thu, 27 Oct 2022 11:12:03 +0300 Subject: [PATCH 01/16] Add products preview feature flag --- Experiments/Experiments/DefaultFeatureFlagService.swift | 2 ++ Experiments/Experiments/FeatureFlag.swift | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/Experiments/Experiments/DefaultFeatureFlagService.swift b/Experiments/Experiments/DefaultFeatureFlagService.swift index c820200da0d..3763e2e29ac 100644 --- a/Experiments/Experiments/DefaultFeatureFlagService.swift +++ b/Experiments/Experiments/DefaultFeatureFlagService.swift @@ -43,6 +43,8 @@ public struct DefaultFeatureFlagService: FeatureFlagService { return buildConfig == .localDeveloper || buildConfig == .alpha case .productsOnboarding: return buildConfig == .localDeveloper || buildConfig == .alpha + case .productsPreview: + return buildConfig == .localDeveloper || buildConfig == .alpha default: return true } diff --git a/Experiments/Experiments/FeatureFlag.swift b/Experiments/Experiments/FeatureFlag.swift index 87938d736db..8b3e542bc1a 100644 --- a/Experiments/Experiments/FeatureFlag.swift +++ b/Experiments/Experiments/FeatureFlag.swift @@ -89,4 +89,8 @@ public enum FeatureFlag: Int { /// Hides products onboarding development. /// case productsOnboarding + + /// Products Onboarding subproject: Products preview. + /// + case productsPreview } From d239cb3ea00d93cb7211e1bc8780cb0eea9ab5da Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Tue, 18 Oct 2022 18:20:12 +0300 Subject: [PATCH 02/16] Add completion closures to saveProduct --- .../Products/Edit Product/ProductFormViewController.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift index ea217926e8a..346a9143432 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift @@ -716,14 +716,14 @@ private extension ProductFormViewController { // MARK: Navigation actions // private extension ProductFormViewController { - func saveProduct(status: ProductStatus? = nil) { + func saveProduct(status: ProductStatus? = nil, onCompletion: @escaping (Result) -> Void = { _ in }) { let productStatus = status ?? product.status let messageType = viewModel.saveMessageType(for: productStatus) showSavingProgress(messageType) - saveProductRemotely(status: status) + saveProductRemotely(status: status, onCompletion: onCompletion) } - func saveProductRemotely(status: ProductStatus?) { + func saveProductRemotely(status: ProductStatus?, onCompletion: @escaping (Result) -> Void = { _ in }) { viewModel.saveProductRemotely(status: status) { [weak self] result in switch result { case .failure(let error): @@ -732,6 +732,7 @@ private extension ProductFormViewController { // Dismisses the in-progress UI then presents the error alert. self?.navigationController?.dismiss(animated: true) { self?.displayError(error: error) + onCompletion(.failure(error)) } case .success: // Dismisses the in-progress UI, then presents the confirmation alert. @@ -741,6 +742,7 @@ private extension ProductFormViewController { // Show linked products promo banner after product save (self?.viewModel as? ProductFormViewModel)?.isLinkedProductsPromoEnabled = true self?.reloadLinkedPromoCellAnimated() + onCompletion(.success(())) } } } From 0e48db905a096a171d1e84a7e002a2f897d13087 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Thu, 27 Oct 2022 14:26:29 +0300 Subject: [PATCH 03/16] Add a preview button in product details --- .../ProductFormViewController.swift | 26 +++++++++++++++++++ .../Edit Product/ProductFormViewModel.swift | 5 ++++ .../ProductFormViewModelProtocol.swift | 1 + 3 files changed, 32 insertions(+) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift index 346a9143432..0713d1f3068 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift @@ -199,6 +199,25 @@ final class ProductFormViewController: saveProduct(status: .draft) } + // MARK: Product preview action handling + + @objc private func saveDraftAndDisplayProductPreview() { + guard viewModel.canSaveAsDraft(), viewModel.hasUnsavedChanges() else { + displayProductPreview() + return + } + + saveProduct(status: .draft) { [weak self] result in + if result.isSuccess { + self?.displayProductPreview() + } + } + } + + private func displayProductPreview() { + // TODO: Show webview + } + // MARK: Navigation actions @objc func closeNavigationBarButtonTapped() { @@ -910,6 +929,8 @@ private extension ProductFormViewController { // Create action buttons based on view model let rightBarButtonItems: [UIBarButtonItem] = viewModel.actionButtons.reversed().map { buttonType in switch buttonType { + case .preview: + return createPreviewBarButtonItem() case .publish: return createPublishBarButtonItem() case .save: @@ -936,6 +957,10 @@ private extension ProductFormViewController { return UIBarButtonItem(title: Localization.saveTitle, style: .done, target: self, action: #selector(saveProductAndLogEvent)) } + func createPreviewBarButtonItem() -> UIBarButtonItem { + return UIBarButtonItem(title: Localization.previewTitle, style: .done, target: self, action: #selector(saveDraftAndDisplayProductPreview)) + } + func createMoreOptionsBarButtonItem() -> UIBarButtonItem { let moreButton = UIBarButtonItem(image: .moreImage, style: .plain, @@ -1537,6 +1562,7 @@ private extension ProductFormViewController { private enum Localization { static let publishTitle = NSLocalizedString("Publish", comment: "Action for creating a new product remotely with a published status") static let saveTitle = NSLocalizedString("Save", comment: "Action for saving a Product remotely") + static let previewTitle = NSLocalizedString("Preview", comment: "Action for previewing draft Product changes in the webview") static let groupedProductsViewTitle = NSLocalizedString("Grouped Products", comment: "Navigation bar title for editing linked products for a grouped product") static let unnamedProduct = NSLocalizedString("Unnamed product", diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift index 40ea9a5ea3f..8b842e759fb 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift @@ -151,6 +151,11 @@ final class ProductFormViewModel: ProductFormViewModelProtocol { } }() + // Preview existing drafts or new products, that can be saved as a draft + if canSaveAsDraft() || productModel.status == .draft { + buttons.insert(.preview, at: 0) + } + // Add more button if needed if shouldShowMoreOptionsMenu() { buttons.append(.more) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModelProtocol.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModelProtocol.swift index 1d8b3e7f6dd..d70563fd392 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModelProtocol.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModelProtocol.swift @@ -10,6 +10,7 @@ enum ProductFormType { /// The type of action that can be performed in the product. enum ActionButtonType { + case preview case publish case save case more From 7fb78a3d97c4e0851df7bc4a337b6c262bf591f0 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Thu, 27 Oct 2022 14:28:09 +0300 Subject: [PATCH 04/16] Hide product preview behind feature flag --- .../Products/Edit Product/ProductFormViewModel.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift index 8b842e759fb..a657b4fe8f9 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift @@ -152,7 +152,8 @@ final class ProductFormViewModel: ProductFormViewModelProtocol { }() // Preview existing drafts or new products, that can be saved as a draft - if canSaveAsDraft() || productModel.status == .draft { + if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.productsPreview), + (canSaveAsDraft() || productModel.status == .draft) { buttons.insert(.preview, at: 0) } From 0b7e9746d1483c6d289bed35bc3196bcbf8eac91 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Thu, 27 Oct 2022 17:05:04 +0300 Subject: [PATCH 05/16] Open product URL with preview parameter --- .../Edit Product/ProductFormViewController.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift index 0713d1f3068..3ce100b9944 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift @@ -215,7 +215,16 @@ final class ProductFormViewController: } private func displayProductPreview() { - // TODO: Show webview + var permalink = URLComponents(string: product.permalink) + var updatedQueryItems = permalink?.queryItems ?? [] + updatedQueryItems.append(.init(name: "preview", value: "true")) + permalink?.queryItems = updatedQueryItems + guard let url = permalink?.url else { + return + } + + // TODO: Show authenticated WebView + WebviewHelper.launch(url, with: self) } // MARK: Navigation actions From 223297d8d074c6bd2055c3895dbcf1b8c230d5fa Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Thu, 27 Oct 2022 17:05:04 +0300 Subject: [PATCH 06/16] Update tests for actionButtons --- .../Edit Product/ProductFormViewModelTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift index 304db3e22e9..0f4accf3bbb 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift @@ -277,7 +277,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.publish, .more]) + XCTAssertEqual(actionButtons, [.preview, .publish, .more]) } func test_action_buttons_for_new_product_with_published_status_and_no_pending_changes() { @@ -289,7 +289,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.publish, .more]) + XCTAssertEqual(actionButtons, [.preview, .publish, .more]) } func test_action_buttons_for_new_product_with_different_status() { @@ -305,7 +305,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.save, .more]) + XCTAssertEqual(actionButtons, [.preview, .save, .more]) } func test_action_buttons_for_existing_published_product_and_pending_changes() { @@ -343,7 +343,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.save, .more]) + XCTAssertEqual(actionButtons, [.preview, .save, .more]) } func test_action_buttons_for_existing_draft_product_and_no_pending_changes() { @@ -355,7 +355,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.publish, .more]) + XCTAssertEqual(actionButtons, [.preview, .publish, .more]) } func test_action_buttons_for_existing_product_with_other_status_and_peding_changes() { From c289b55627dad55617caedf3e6eb42b9e1bb21a4 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Thu, 27 Oct 2022 17:21:31 +0300 Subject: [PATCH 07/16] Fix save trigger condition for preview drafts --- .../Products/Edit Product/ProductFormViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift index 3ce100b9944..802b368539c 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift @@ -202,7 +202,7 @@ final class ProductFormViewController: // MARK: Product preview action handling @objc private func saveDraftAndDisplayProductPreview() { - guard viewModel.canSaveAsDraft(), viewModel.hasUnsavedChanges() else { + guard viewModel.canSaveAsDraft() || viewModel.hasUnsavedChanges() else { displayProductPreview() return } From a525828976e1f8518016f895910e74b3f8f3d2d1 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Thu, 27 Oct 2022 20:02:23 +0300 Subject: [PATCH 08/16] Revert "Update tests for actionButtons" 223297d8d074c6bd2055c3895dbcf1b8c230d5fa --- .../Edit Product/ProductFormViewModelTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift index 0f4accf3bbb..304db3e22e9 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift @@ -277,7 +277,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.preview, .publish, .more]) + XCTAssertEqual(actionButtons, [.publish, .more]) } func test_action_buttons_for_new_product_with_published_status_and_no_pending_changes() { @@ -289,7 +289,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.preview, .publish, .more]) + XCTAssertEqual(actionButtons, [.publish, .more]) } func test_action_buttons_for_new_product_with_different_status() { @@ -305,7 +305,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.preview, .save, .more]) + XCTAssertEqual(actionButtons, [.save, .more]) } func test_action_buttons_for_existing_published_product_and_pending_changes() { @@ -343,7 +343,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.preview, .save, .more]) + XCTAssertEqual(actionButtons, [.save, .more]) } func test_action_buttons_for_existing_draft_product_and_no_pending_changes() { @@ -355,7 +355,7 @@ final class ProductFormViewModelTests: XCTestCase { let actionButtons = viewModel.actionButtons // Then - XCTAssertEqual(actionButtons, [.preview, .publish, .more]) + XCTAssertEqual(actionButtons, [.publish, .more]) } func test_action_buttons_for_existing_product_with_other_status_and_peding_changes() { From edf3702087b4d3bb9e2470031bf324b992edd9f2 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Fri, 28 Oct 2022 13:28:54 +0300 Subject: [PATCH 09/16] Use productsPreview feature flag --- Experiments/Experiments/DefaultFeatureFlagService.swift | 2 -- Experiments/Experiments/FeatureFlag.swift | 4 ---- .../Products/Edit Product/ProductFormViewModel.swift | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Experiments/Experiments/DefaultFeatureFlagService.swift b/Experiments/Experiments/DefaultFeatureFlagService.swift index 598bbb9112e..f19455126b4 100644 --- a/Experiments/Experiments/DefaultFeatureFlagService.swift +++ b/Experiments/Experiments/DefaultFeatureFlagService.swift @@ -47,8 +47,6 @@ public struct DefaultFeatureFlagService: FeatureFlagService { return buildConfig == .localDeveloper || buildConfig == .alpha case .productsOnboarding: return buildConfig == .localDeveloper || buildConfig == .alpha - case .productsPreview: - return buildConfig == .localDeveloper || buildConfig == .alpha case .simplifiedLoginFlowI1: return buildConfig == .localDeveloper || buildConfig == .alpha default: diff --git a/Experiments/Experiments/FeatureFlag.swift b/Experiments/Experiments/FeatureFlag.swift index 9194ccdabc6..8977b1c60cd 100644 --- a/Experiments/Experiments/FeatureFlag.swift +++ b/Experiments/Experiments/FeatureFlag.swift @@ -90,10 +90,6 @@ public enum FeatureFlag: Int { /// case productsOnboarding - /// Products Onboarding subproject: Products preview. - /// - case productsPreview - /// Temporary feature flag for the simplified login flow. /// TODO: replace with A/B testing. /// diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift index a657b4fe8f9..04e2964c1eb 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift @@ -152,7 +152,7 @@ final class ProductFormViewModel: ProductFormViewModelProtocol { }() // Preview existing drafts or new products, that can be saved as a draft - if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.productsPreview), + if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.productsOnboarding), (canSaveAsDraft() || productModel.status == .draft) { buttons.insert(.preview, at: 0) } From b6ea8997c81fdf5b02f82ba427333ab452579e20 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Fri, 28 Oct 2022 13:37:36 +0300 Subject: [PATCH 10/16] Do not preview new blank products without any changes --- .../Products/Edit Product/ProductFormViewModel.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift index 04e2964c1eb..38a7ba80363 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift @@ -153,7 +153,9 @@ final class ProductFormViewModel: ProductFormViewModelProtocol { // Preview existing drafts or new products, that can be saved as a draft if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.productsOnboarding), - (canSaveAsDraft() || productModel.status == .draft) { + (canSaveAsDraft() || productModel.status == .draft), + // Do not preview new blank products without any changes + !(formType == .add && !hasUnsavedChanges()) { buttons.insert(.preview, at: 0) } From c1e812fb3667e152e578881e25e2b4084e2813a6 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Fri, 28 Oct 2022 13:37:50 +0300 Subject: [PATCH 11/16] Move comment --- .../Products/Edit Product/ProductFormViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift index 38a7ba80363..1a7ec2f17ff 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift @@ -151,8 +151,8 @@ final class ProductFormViewModel: ProductFormViewModelProtocol { } }() - // Preview existing drafts or new products, that can be saved as a draft if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.productsOnboarding), + // Preview existing drafts or new products, that can be saved as a draft (canSaveAsDraft() || productModel.status == .draft), // Do not preview new blank products without any changes !(formType == .add && !hasUnsavedChanges()) { From 98679280a64e8269b8ec666fac90823a9780e7ab Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Fri, 28 Oct 2022 13:50:10 +0300 Subject: [PATCH 12/16] Do not preview published products with changed status --- .../Products/Edit Product/ProductFormViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift index 1a7ec2f17ff..bba6844aa0d 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift @@ -153,7 +153,7 @@ final class ProductFormViewModel: ProductFormViewModelProtocol { if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.productsOnboarding), // Preview existing drafts or new products, that can be saved as a draft - (canSaveAsDraft() || productModel.status == .draft), + (canSaveAsDraft() || originalProductModel.status == .draft), // Do not preview new blank products without any changes !(formType == .add && !hasUnsavedChanges()) { buttons.insert(.preview, at: 0) From 6281f5bb2c1c2a3ddb723a676c966133043e45b8 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Fri, 28 Oct 2022 17:13:39 +0300 Subject: [PATCH 13/16] Inject FeatureFlagService dependency in ProductFormViewModel --- .../Products/Edit Product/ProductFormViewModel.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift index bba6844aa0d..ab317307cf7 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewModel.swift @@ -1,4 +1,5 @@ import Combine +import protocol Experiments.FeatureFlagService import Yosemite import protocol Storage.StorageManagerType @@ -151,7 +152,7 @@ final class ProductFormViewModel: ProductFormViewModelProtocol { } }() - if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.productsOnboarding), + if featureFlagService.isFeatureFlagEnabled(.productsOnboarding), // Preview existing drafts or new products, that can be saved as a draft (canSaveAsDraft() || originalProductModel.status == .draft), // Do not preview new blank products without any changes @@ -178,13 +179,16 @@ final class ProductFormViewModel: ProductFormViewModelProtocol { private let analytics: Analytics + private let featureFlagService: FeatureFlagService + init(product: EditableProductModel, formType: ProductFormType, productImageActionHandler: ProductImageActionHandler, stores: StoresManager = ServiceLocator.stores, storageManager: StorageManagerType = ServiceLocator.storageManager, productImagesUploader: ProductImageUploaderProtocol = ServiceLocator.productImageUploader, - analytics: Analytics = ServiceLocator.analytics) { + analytics: Analytics = ServiceLocator.analytics, + featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService) { self.formType = formType self.productImageActionHandler = productImageActionHandler self.originalProduct = product @@ -194,6 +198,7 @@ final class ProductFormViewModel: ProductFormViewModelProtocol { self.storageManager = storageManager self.productImagesUploader = productImagesUploader self.analytics = analytics + self.featureFlagService = featureFlagService self.cancellable = productImageActionHandler.addUpdateObserver(self) { [weak self] allStatuses in guard let self = self else { return } From 9401ad1f6f7d0ad31bb42e147f19b5cf53d5d040 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Fri, 28 Oct 2022 17:20:42 +0300 Subject: [PATCH 14/16] Fix ProductFormViewModelTests to use mocks with disabled feature flags --- .../Products/Edit Product/ProductFormViewModelTests.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift index 304db3e22e9..d016dc121b4 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift @@ -3,6 +3,7 @@ import XCTest @testable import WooCommerce import Yosemite import TestKit +import protocol Experiments.FeatureFlagService final class ProductFormViewModelTests: XCTestCase { @@ -538,13 +539,15 @@ private extension ProductFormViewModelTests { func createViewModel(product: Product, formType: ProductFormType, stores: StoresManager = ServiceLocator.stores, - analytics: Analytics = ServiceLocator.analytics) -> ProductFormViewModel { + analytics: Analytics = ServiceLocator.analytics, + featureFlagService: FeatureFlagService = MockFeatureFlagService()) -> ProductFormViewModel { let model = EditableProductModel(product: product) let productImageActionHandler = ProductImageActionHandler(siteID: 0, product: model) return ProductFormViewModel(product: model, formType: formType, productImageActionHandler: productImageActionHandler, stores: stores, - analytics: analytics) + analytics: analytics, + featureFlagService: featureFlagService) } } From ac52d2df880c8972ee53b762055c5906ff8ee70b Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Fri, 28 Oct 2022 17:21:05 +0300 Subject: [PATCH 15/16] Add productsOnboarding support to MockFeatureFlagService --- .../WooCommerceTests/Mocks/MockFeatureFlagService.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/WooCommerce/WooCommerceTests/Mocks/MockFeatureFlagService.swift b/WooCommerce/WooCommerceTests/Mocks/MockFeatureFlagService.swift index aec4efe0e3b..be2b38fae2f 100644 --- a/WooCommerce/WooCommerceTests/Mocks/MockFeatureFlagService.swift +++ b/WooCommerce/WooCommerceTests/Mocks/MockFeatureFlagService.swift @@ -8,19 +8,22 @@ struct MockFeatureFlagService: FeatureFlagService { private let shippingLabelsOnboardingM1: Bool private let isLoginPrologueOnboardingEnabled: Bool private let isSimplifiedLoginFlowI1Enabled: Bool + private let isProductsOnboardingEnabled: Bool init(isInboxOn: Bool = false, isSplitViewInOrdersTabOn: Bool = false, isUpdateOrderOptimisticallyOn: Bool = false, shippingLabelsOnboardingM1: Bool = false, isLoginPrologueOnboardingEnabled: Bool = false, - isSimplifiedLoginFlowI1Enabled: Bool = false) { + isSimplifiedLoginFlowI1Enabled: Bool = false, + isProductsOnboardingEnabled: Bool = false) { self.isInboxOn = isInboxOn self.isSplitViewInOrdersTabOn = isSplitViewInOrdersTabOn self.isUpdateOrderOptimisticallyOn = isUpdateOrderOptimisticallyOn self.shippingLabelsOnboardingM1 = shippingLabelsOnboardingM1 self.isLoginPrologueOnboardingEnabled = isLoginPrologueOnboardingEnabled self.isSimplifiedLoginFlowI1Enabled = isSimplifiedLoginFlowI1Enabled + self.isProductsOnboardingEnabled = isProductsOnboardingEnabled } func isFeatureFlagEnabled(_ featureFlag: FeatureFlag) -> Bool { @@ -37,6 +40,8 @@ struct MockFeatureFlagService: FeatureFlagService { return isLoginPrologueOnboardingEnabled case .simplifiedLoginFlowI1: return isSimplifiedLoginFlowI1Enabled + case .productsOnboarding: + return isProductsOnboardingEnabled default: return false } From d69489cfa52cd3f908d2081ffc56e82bd056b800 Mon Sep 17 00:00:00 2001 From: Evgeny Aleksandrov Date: Fri, 28 Oct 2022 17:38:54 +0300 Subject: [PATCH 16/16] Add unit tests for preview button logic in ProductFormViewModel --- .../ProductFormViewModelTests.swift | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift index d016dc121b4..5131a180196 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Products/Edit Product/ProductFormViewModelTests.swift @@ -533,6 +533,121 @@ final class ProductFormViewModelTests: XCTestCase { let hasLinkedProducts = try XCTUnwrap(analyticsProvider.receivedProperties.first?["has_linked_products"] as? Bool) XCTAssertTrue(hasLinkedProducts) } + + // MARK: Preview button tests (with enabled Product Onboarding feature flag) + + func test_no_preview_button_for_new_blank_product_without_any_changes() { + // Given + let product = Product.fake().copy(statusKey: ProductStatus.published.rawValue) + let viewModel = createViewModel(product: product, formType: .add, featureFlagService: MockFeatureFlagService(isProductsOnboardingEnabled: true)) + + // When + let actionButtons = viewModel.actionButtons + + // Then + XCTAssertEqual(actionButtons, [.publish, .more]) + } + + func test_preview_button_for_new_product_with_pending_changes() { + // Given + let product = Product.fake().copy(statusKey: ProductStatus.published.rawValue) + let viewModel = createViewModel(product: product, formType: .add, featureFlagService: MockFeatureFlagService(isProductsOnboardingEnabled: true)) + viewModel.updateName("new name") + + // When + let actionButtons = viewModel.actionButtons + + // Then + XCTAssertEqual(actionButtons, [.preview, .publish, .more]) + } + + func test_no_preview_button_for_existing_published_product_without_any_changes() { + // Given + let product = Product.fake().copy(productID: 123, statusKey: ProductStatus.published.rawValue) + let viewModel = createViewModel(product: product, formType: .edit, featureFlagService: MockFeatureFlagService(isProductsOnboardingEnabled: true)) + viewModel.updateName("new name") + + // When + let actionButtons = viewModel.actionButtons + + // Then + XCTAssertEqual(actionButtons, [.save, .more]) + } + + func test_no_preview_button_for_existing_published_product_with_pending_changes() { + // Given + let product = Product.fake().copy(productID: 123, statusKey: ProductStatus.published.rawValue) + let viewModel = createViewModel(product: product, formType: .edit, featureFlagService: MockFeatureFlagService(isProductsOnboardingEnabled: true)) + + // When + let actionButtons = viewModel.actionButtons + + // Then + XCTAssertEqual(actionButtons, [.more]) + } + + func test_preview_button_for_existing_draft_product_without_any_changes() { + // Given + let product = Product.fake().copy(productID: 123, statusKey: ProductStatus.draft.rawValue) + let viewModel = createViewModel(product: product, formType: .edit, featureFlagService: MockFeatureFlagService(isProductsOnboardingEnabled: true)) + + // When + let actionButtons = viewModel.actionButtons + + // Then + XCTAssertEqual(actionButtons, [.preview, .publish, .more]) + } + + func test_preview_button_for_existing_draft_product_with_pending_changes() { + // Given + let product = Product.fake().copy(productID: 123, statusKey: ProductStatus.draft.rawValue) + let viewModel = createViewModel(product: product, formType: .edit, featureFlagService: MockFeatureFlagService(isProductsOnboardingEnabled: true)) + viewModel.updateName("new name") + + // When + let actionButtons = viewModel.actionButtons + + // Then + XCTAssertEqual(actionButtons, [.preview, .save, .more]) + } + + func test_no_preview_button_for_existing_product_with_other_status_and_without_any_changes() { + // Given + let product = Product.fake().copy(productID: 123, statusKey: "other") + let viewModel = createViewModel(product: product, formType: .edit, featureFlagService: MockFeatureFlagService(isProductsOnboardingEnabled: true)) + + // When + let actionButtons = viewModel.actionButtons + + // Then + XCTAssertEqual(actionButtons, [.publish, .more]) + } + + func test_no_preview_button_for_existing_product_with_other_status_and_pending_changes() { + // Given + let product = Product.fake().copy(productID: 123, statusKey: "other") + let viewModel = createViewModel(product: product, formType: .edit, featureFlagService: MockFeatureFlagService(isProductsOnboardingEnabled: true)) + viewModel.updateName("new name") + + // When + let actionButtons = viewModel.actionButtons + + // Then + XCTAssertEqual(actionButtons, [.save, .more]) + } + + func test_no_preview_button_for_any_product_in_read_only_mode() { + // Given + let product = Product.fake().copy(productID: 123, statusKey: ProductStatus.published.rawValue) + let viewModel = createViewModel(product: product, formType: .readonly, featureFlagService: MockFeatureFlagService(isProductsOnboardingEnabled: true)) + viewModel.updateName("new name") + + // When + let actionButtons = viewModel.actionButtons + + // Then + XCTAssertEqual(actionButtons, [.more]) + } } private extension ProductFormViewModelTests {