diff --git a/Networking/Networking/Model/Product/ProductAttribute.swift b/Networking/Networking/Model/Product/ProductAttribute.swift index e5fd3035994..f6c57b5b6e9 100644 --- a/Networking/Networking/Model/Product/ProductAttribute.swift +++ b/Networking/Networking/Model/Product/ProductAttribute.swift @@ -120,6 +120,14 @@ extension ProductAttribute: Comparable { } } +extension ProductAttribute: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(siteID) + hasher.combine(name) + hasher.combine(attributeID) + } +} + // MARK: - Decoding Errors // enum ProductAttributeDecodingError: Error { diff --git a/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift b/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift index dd3b9e05bc0..4f2b98bfac6 100644 --- a/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift +++ b/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift @@ -2,7 +2,6 @@ import Foundation import Combine import enum Yosemite.POSItem import protocol Yosemite.PointOfSaleItemServiceProtocol -import enum Yosemite.PointOfSaleProductServiceError import struct Yosemite.POSParentProduct import class Yosemite.Store diff --git a/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift b/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift index 9e7af342ed2..ace05c868ea 100644 --- a/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift +++ b/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift @@ -77,7 +77,7 @@ private extension ChildItemList { name: "Variable latte", productImageSource: nil, productID: 1, - type: .variable + type: .variable(.init()) ) let parentItem = POSItem.parentProduct(parentProduct) let itemsController = PointOfSalePreviewItemsController() diff --git a/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift b/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift index b220384baf8..3328e9f845a 100644 --- a/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift +++ b/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift @@ -62,7 +62,7 @@ private struct ItemListRow: View { name: "Variable mocha", productImageSource: "https://pd.w.org/2024/12/986762d0d4d4cf17.82435881-scaled.jpeg", productID: 16, - type: .variable + type: .variable(.init()) ) ) ] diff --git a/WooCommerce/Classes/POS/Presentation/ParentProductCardView.swift b/WooCommerce/Classes/POS/Presentation/ParentProductCardView.swift index 14fc12af9e1..b4e24af07ce 100644 --- a/WooCommerce/Classes/POS/Presentation/ParentProductCardView.swift +++ b/WooCommerce/Classes/POS/Presentation/ParentProductCardView.swift @@ -76,7 +76,13 @@ private extension ParentProductCardView { #if DEBUG #Preview { - let parentProduct = POSParentProduct(id: UUID(), name: "Parent variable product", productImageSource: nil, productID: 42, type: .variable) + let parentProduct = POSParentProduct( + id: UUID(), + name: "Parent variable product", + productImageSource: nil, + productID: 42, + type: .variable(.init()) + ) ParentProductCardView(parentProduct: parentProduct) } #endif diff --git a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift index c86f698b4f1..139c4559739 100644 --- a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift +++ b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift @@ -101,7 +101,7 @@ private var mockItems: [POSItem] { name: "Variable product 1", productImageSource: nil, productID: 5, - type: .variable + type: .variable(.init()) ) ), .simpleProduct(POSSimpleProduct(id: UUID(), name: "Product 4", formattedPrice: "$4.00", productID: 4, price: "4.00")) diff --git a/WooCommerce/Classes/ViewModels/ProductDetailsCellViewModel.swift b/WooCommerce/Classes/ViewModels/ProductDetailsCellViewModel.swift index ab3421031bb..60c4ba086db 100644 --- a/WooCommerce/Classes/ViewModels/ProductDetailsCellViewModel.swift +++ b/WooCommerce/Classes/ViewModels/ProductDetailsCellViewModel.swift @@ -2,48 +2,6 @@ import Foundation import Yosemite import WooFoundation -// MARK: - View Model for a Variation Attribute -// -struct VariationAttributeViewModel: Equatable { - - /// Attribute name - /// - let name: String - - /// Attribute value - /// - let value: String? - - /// Returns the attribute value, or "Any \(name)" if the attribute value is nil or empty - /// - var nameOrValue: String { - guard let value = value, value.isNotEmpty else { - return String(format: Localization.anyAttributeFormat, name) - } - return value - } - - init(name: String, value: String? = nil) { - self.name = name - self.value = value - } - - init(orderItemAttribute: OrderItemAttribute) { - self.init(name: orderItemAttribute.name, value: orderItemAttribute.value) - } - - init(productVariationAttribute: ProductVariationAttribute) { - self.init(name: productVariationAttribute.name, value: productVariationAttribute.option) - } -} - -extension VariationAttributeViewModel { - enum Localization { - static let anyAttributeFormat = - NSLocalizedString("Any %1$@", comment: "Format of a product variation attribute description where the attribute is set to any value.") - } -} - // MARK: - View Model for a product details cell // diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 54f418f569c..5daf7a66829 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -2178,7 +2178,6 @@ CCE73D2529EDAB5C0064E797 /* SubscriptionPeriod+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE73D2429EDAB5C0064E797 /* SubscriptionPeriod+UI.swift */; }; CCE785C829C1E8280003977F /* BundledProductsListViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE785C729C1E8280003977F /* BundledProductsListViewModelTests.swift */; }; CCE785CA29C1F9170003977F /* ProductBundleItemStockStatus+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE785C929C1F9170003977F /* ProductBundleItemStockStatus+UI.swift */; }; - CCEC256A27B581E800EF9FA3 /* ProductVariationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCEC256927B581E800EF9FA3 /* ProductVariationFormatter.swift */; }; CCF27B35280EF69700B755E1 /* orders_3337_add_product.json in Resources */ = {isa = PBXBuildFile; fileRef = CCF27B33280EF69600B755E1 /* orders_3337_add_product.json */; }; CCF27B3A280EF98F00B755E1 /* orders_3337.json in Resources */ = {isa = PBXBuildFile; fileRef = CCF27B39280EF98F00B755E1 /* orders_3337.json */; }; CCF87BBE279047BC00461C43 /* InfiniteScrollList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF87BBD279047BC00461C43 /* InfiniteScrollList.swift */; }; @@ -5314,7 +5313,6 @@ CCE73D2429EDAB5C0064E797 /* SubscriptionPeriod+UI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SubscriptionPeriod+UI.swift"; sourceTree = ""; }; CCE785C729C1E8280003977F /* BundledProductsListViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundledProductsListViewModelTests.swift; sourceTree = ""; }; CCE785C929C1F9170003977F /* ProductBundleItemStockStatus+UI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProductBundleItemStockStatus+UI.swift"; sourceTree = ""; }; - CCEC256927B581E800EF9FA3 /* ProductVariationFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductVariationFormatter.swift; sourceTree = ""; }; CCF27B33280EF69600B755E1 /* orders_3337_add_product.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = orders_3337_add_product.json; sourceTree = ""; }; CCF27B39280EF98F00B755E1 /* orders_3337.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = orders_3337.json; sourceTree = ""; }; CCF87BBD279047BC00461C43 /* InfiniteScrollList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfiniteScrollList.swift; sourceTree = ""; }; @@ -12080,7 +12078,6 @@ D817585C22BB5E6900289CFE /* Order Details */, 0371C36C2876E91500277E2C /* Feature Announcement Cards */, D843D5D82248EE90001BFA55 /* ManualTrackingViewModel.swift */, - CCEC256927B581E800EF9FA3 /* ProductVariationFormatter.swift */, CECC758B23D2227000486676 /* ProductDetailsCellViewModel.swift */, D843D5D622485B19001BFA55 /* ShippingProvidersViewModel.swift */, D8736B5922F07D7100A14A29 /* MainTabViewModel.swift */, @@ -15818,7 +15815,6 @@ 02E8B17C23E2C78A00A43403 /* ProductImageStatus.swift in Sources */, 03F5CB832A0C3A1A0026877A /* AnimatedPlaceholder.swift in Sources */, 0259D5FF2581F3FA003B1CD6 /* ShippingLabelPaperSizeOptionsViewController.swift in Sources */, - CCEC256A27B581E800EF9FA3 /* ProductVariationFormatter.swift in Sources */, 02EA6BFA2435E92600FFF90A /* KingfisherImageDownloader+ImageDownloadable.swift in Sources */, 7E7C5F8F2719BA7300315B61 /* ProductCategoryCellViewModel.swift in Sources */, DE8AA0B32BBE55E40084D2CC /* DashboardViewHostingController.swift in Sources */, diff --git a/Yosemite/Yosemite.xcodeproj/project.pbxproj b/Yosemite/Yosemite.xcodeproj/project.pbxproj index cad3fd97ca5..7c410ed24e8 100644 --- a/Yosemite/Yosemite.xcodeproj/project.pbxproj +++ b/Yosemite/Yosemite.xcodeproj/project.pbxproj @@ -81,6 +81,8 @@ 02C254FA2563B66600A04423 /* ShippingLabelRefund+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C254F92563B66600A04423 /* ShippingLabelRefund+ReadOnlyConvertible.swift */; }; 02C254FE2563C6E500A04423 /* ShippingLabelSettings+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C254FD2563C6E500A04423 /* ShippingLabelSettings+ReadOnlyConvertible.swift */; }; 02C255022563C76A00A04423 /* ShippingLabel+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C255012563C76A00A04423 /* ShippingLabel+ReadOnlyConvertible.swift */; }; + 02CC7C2C2D2CE5CB00907B83 /* ProductVariationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CC7C2B2D2CE5CB00907B83 /* ProductVariationFormatter.swift */; }; + 02CC7C2E2D2CE5F600907B83 /* VariationAttributeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CC7C2D2D2CE5F600907B83 /* VariationAttributeViewModel.swift */; }; 02DAE7F8291A9F11009342B7 /* DomainStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DAE7F7291A9F11009342B7 /* DomainStoreTests.swift */; }; 02DAE7FA291A9F36009342B7 /* MockDomainRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DAE7F9291A9F36009342B7 /* MockDomainRemote.swift */; }; 02DF98092A136BFB0009E2EA /* MockSitePluginsRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DF98082A136BFB0009E2EA /* MockSitePluginsRemote.swift */; }; @@ -618,6 +620,8 @@ 02C254F92563B66600A04423 /* ShippingLabelRefund+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShippingLabelRefund+ReadOnlyConvertible.swift"; sourceTree = ""; }; 02C254FD2563C6E500A04423 /* ShippingLabelSettings+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShippingLabelSettings+ReadOnlyConvertible.swift"; sourceTree = ""; }; 02C255012563C76A00A04423 /* ShippingLabel+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShippingLabel+ReadOnlyConvertible.swift"; sourceTree = ""; }; + 02CC7C2B2D2CE5CB00907B83 /* ProductVariationFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductVariationFormatter.swift; sourceTree = ""; }; + 02CC7C2D2D2CE5F600907B83 /* VariationAttributeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariationAttributeViewModel.swift; sourceTree = ""; }; 02DAE7F7291A9F11009342B7 /* DomainStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainStoreTests.swift; sourceTree = ""; }; 02DAE7F9291A9F36009342B7 /* MockDomainRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDomainRemote.swift; sourceTree = ""; }; 02DF98082A136BFB0009E2EA /* MockSitePluginsRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSitePluginsRemote.swift; sourceTree = ""; }; @@ -2065,6 +2069,8 @@ isa = PBXGroup; children = ( B9C0C1072A3C666A00DF84EA /* ProductVariationStorageManager.swift */, + 02CC7C2B2D2CE5CB00907B83 /* ProductVariationFormatter.swift */, + 02CC7C2D2D2CE5F600907B83 /* VariationAttributeViewModel.swift */, ); path = ProductVariations; sourceTree = ""; @@ -2383,6 +2389,7 @@ CE0FBB252D0C65EB008B7789 /* WooShippingCustomPackage+ReadOnlyConvertible.swift in Sources */, CE3B7AD72225ECA90050FE4B /* OrderStatusStore.swift in Sources */, B5631ECD2114DF8C008D3535 /* EntityListener.swift in Sources */, + 02CC7C2E2D2CE5F600907B83 /* VariationAttributeViewModel.swift in Sources */, D80F758A223F72AA002F4A3B /* ShipmentTrackingProviderGroup+ReadOnlyConvertible.swift in Sources */, B9AECD442851D95600E78584 /* TotalRefundedCalculationUseCase.swift in Sources */, 86DAA7982CD9E31E002AE55E /* MockPaymentActionHandler.swift in Sources */, @@ -2600,6 +2607,7 @@ 0232372922F7DA6E00715FAB /* StatsTimeRangeV4.swift in Sources */, 247CE85C25832A5000F9D9D1 /* MockProductVariationActionHandler.swift in Sources */, CEC7D5992CDE360E00111B79 /* WooShippingStore.swift in Sources */, + 02CC7C2C2D2CE5CB00907B83 /* ProductVariationFormatter.swift in Sources */, B5F2AE9720EBB54A00FEDC59 /* FetchedResultsControllerDelegateWrapper.swift in Sources */, 247CE7C02582DC7200F9D9D1 /* ProductImage+Mocks.swift in Sources */, 7492FADF217FB11D00ED2C69 /* SettingAction.swift in Sources */, diff --git a/Yosemite/Yosemite/PointOfSale/POSParentProduct.swift b/Yosemite/Yosemite/PointOfSale/POSParentProduct.swift index 7c1e32fbb7f..eeeaa178270 100644 --- a/Yosemite/Yosemite/PointOfSale/POSParentProduct.swift +++ b/Yosemite/Yosemite/PointOfSale/POSParentProduct.swift @@ -1,7 +1,22 @@ import Foundation -public enum POSParentProductType { - case variable +public enum POSParentProductType: Equatable, Hashable { + case variable(POSVariableParentProduct) +} + +public struct POSVariableParentProduct: Equatable, Hashable { + let allAttributes: [ProductAttribute] + + init(allAttributes: [ProductAttribute]) { + self.allAttributes = allAttributes + } + +#if DEBUG + /// Initializer for SwiftUI previews. + public init() { + allAttributes = [] + } +#endif } public struct POSParentProduct: Equatable, Hashable, Identifiable { diff --git a/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift b/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift index 453c1b3d1dc..d9e50e863e5 100644 --- a/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift +++ b/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift @@ -7,8 +7,9 @@ import class Networking.AlamofireNetwork import class WooFoundation.CurrencyFormatter import class WooFoundation.CurrencySettings -public enum PointOfSaleProductServiceError: Error { +public enum PointOfSaleItemServiceError: Error, Equatable { case requestFailed + case invalidParentProduct(POSParentProduct) case unknown } @@ -65,16 +66,25 @@ public final class PointOfSaleItemService: PointOfSaleItemServiceProtocol { } public func providePointOfSaleVariationItems(for parentProduct: POSParentProduct, pageNumber: Int) async throws -> PagedItems { + guard case let .variable(variableProduct) = parentProduct.type else { + assertionFailure( + "Unexpected parent product when loading variations: \(parentProduct)" + ) + throw PointOfSaleItemServiceError.invalidParentProduct(parentProduct) + } let variations = try await variationRemote .loadVariationsForPointOfSale(for: siteID, parentProductID: parentProduct.productID, pageNumber: pageNumber) return .init( items: variations.compactMap({ variation in - POSItem + let variationName = ProductVariationFormatter().generateName( + for: variation, + from: variableProduct.allAttributes + ) + return POSItem .variation(.init(id: UUID(), - // TODO-14702: variation name with ProductVariationFormatter - name: "Variation \(variation.productVariationID)", + name: variationName, formattedPrice: currencyFormatter.formatAmount(variation.price) ?? "-", productImageSource: variation.image?.src)) }), @@ -104,7 +114,7 @@ public final class PointOfSaleItemService: PointOfSaleItemServiceProtocol { name: product.name, productImageSource: thumbnailSource, productID: product.productID, - type: .variable)) + type: .variable(.init(allAttributes: product.attributesForVariations)))) default: return nil } diff --git a/WooCommerce/Classes/ViewModels/ProductVariationFormatter.swift b/Yosemite/Yosemite/Tools/ProductVariations/ProductVariationFormatter.swift similarity index 81% rename from WooCommerce/Classes/ViewModels/ProductVariationFormatter.swift rename to Yosemite/Yosemite/Tools/ProductVariations/ProductVariationFormatter.swift index 06ba7d464f2..9039bc667ec 100644 --- a/WooCommerce/Classes/ViewModels/ProductVariationFormatter.swift +++ b/Yosemite/Yosemite/Tools/ProductVariations/ProductVariationFormatter.swift @@ -1,16 +1,16 @@ import Foundation -import Yosemite /// Helper to format product variation details, such as variation name or attributes. /// -struct ProductVariationFormatter { +public struct ProductVariationFormatter { + public init() {} /// Generates a name for the product variation, given a list of the parent product attributes, e.g. "Blue - Any Size" /// - Parameters: /// - variation: The product variation whose name is being generated /// - allAttributes: A list of attributes from the parent `Product` /// - func generateName(for variation: ProductVariation, from allAttributes: [ProductAttribute]) -> String { + public func generateName(for variation: ProductVariation, from allAttributes: [ProductAttribute]) -> String { let variationAttributes = generateAttributes(for: variation, from: allAttributes) return variationAttributes.map { $0.nameOrValue }.joined(separator: " - ") } @@ -20,7 +20,7 @@ struct ProductVariationFormatter { /// - variation: The product variation whose attributes are being generated /// - allAttributes: A list of attributes from the parent `Product` /// - func generateAttributes(for variation: ProductVariation, from allAttributes: [ProductAttribute]) -> [VariationAttributeViewModel] { + public func generateAttributes(for variation: ProductVariation, from allAttributes: [ProductAttribute]) -> [VariationAttributeViewModel] { return allAttributes .sorted(by: { (lhs, rhs) -> Bool in lhs.position < rhs.position diff --git a/Yosemite/Yosemite/Tools/ProductVariations/VariationAttributeViewModel.swift b/Yosemite/Yosemite/Tools/ProductVariations/VariationAttributeViewModel.swift new file mode 100644 index 00000000000..f514c456832 --- /dev/null +++ b/Yosemite/Yosemite/Tools/ProductVariations/VariationAttributeViewModel.swift @@ -0,0 +1,42 @@ +import Foundation + +/// View Model for a Variation Attribute. +public struct VariationAttributeViewModel: Equatable { + + /// Attribute name + /// + public let name: String + + /// Attribute value + /// + public let value: String? + + /// Returns the attribute value, or "Any \(name)" if the attribute value is nil or empty + /// + public var nameOrValue: String { + guard let value = value, !value.isEmpty else { + return String(format: Localization.anyAttributeFormat, name) + } + return value + } + + public init(name: String, value: String? = nil) { + self.name = name + self.value = value + } + + public init(orderItemAttribute: OrderItemAttribute) { + self.init(name: orderItemAttribute.name, value: orderItemAttribute.value) + } + + init(productVariationAttribute: ProductVariationAttribute) { + self.init(name: productVariationAttribute.name, value: productVariationAttribute.option) + } +} + +extension VariationAttributeViewModel { + enum Localization { + static let anyAttributeFormat = + NSLocalizedString("Any %1$@", comment: "Format of a product variation attribute description where the attribute is set to any value.") + } +} diff --git a/Yosemite/YosemiteTests/PointOfSale/PointOfSaleItemServiceTests.swift b/Yosemite/YosemiteTests/PointOfSale/PointOfSaleItemServiceTests.swift index 640e1a45572..28b7686194f 100644 --- a/Yosemite/YosemiteTests/PointOfSale/PointOfSaleItemServiceTests.swift +++ b/Yosemite/YosemiteTests/PointOfSale/PointOfSaleItemServiceTests.swift @@ -27,7 +27,7 @@ final class PointOfSaleItemServiceTests: XCTestCase { func test_PointOfSaleItemServiceProtocol_when_fails_request_with_requestFailed_then_throws_error() async throws { // Given - let expectedError = PointOfSaleProductServiceError.requestFailed + let expectedError = PointOfSaleItemServiceError.requestFailed network.simulateError(requestUrlSuffix: "products", error: expectedError) // When @@ -36,7 +36,7 @@ final class PointOfSaleItemServiceTests: XCTestCase { XCTFail("Expected an error, but got success.") } catch { // Then - XCTAssertEqual(error as? PointOfSaleProductServiceError, expectedError) + XCTAssertEqual(error as? PointOfSaleItemServiceError, expectedError) } } @@ -153,7 +153,46 @@ final class PointOfSaleItemServiceTests: XCTestCase { name: "Tea", productImageSource: nil, productID: parentProductID, - type: .variable + type: .variable( + .init(allAttributes: [ + .init( + siteID: siteID, + attributeID: 0, + name: "Shape", + position: 1, + visible: true, + variation: true, + options: ["Marble", "Heart"] + ), + .init( + siteID: siteID, + attributeID: 0, + name: "Flavor", + position: 2, + visible: true, + variation: true, + options: ["fruity", "nuts"] + ), + .init( + siteID: siteID, + attributeID: 0, + name: "Darkness", + position: 3, + visible: true, + variation: true, + options: ["99%", "87%"] + ), + .init( + siteID: siteID, + attributeID: 0, + name: "Size", + position: 4, + visible: true, + variation: true, + options: ["6 piece"] + ) + ] + )) ), pageNumber: 1 ) @@ -166,7 +205,10 @@ final class PointOfSaleItemServiceTests: XCTestCase { guard case let .variation(firstVariation) = firstVariation else { return XCTFail("Variation is expected.") } - XCTAssertEqual(firstVariation.name, "Variation 1275") + XCTAssertEqual( + firstVariation.name, + "marble - nuts - 99% - \(String.localizedStringWithFormat(VariationAttributeViewModel.Localization.anyAttributeFormat, "Size"))" + ) XCTAssertEqual(firstVariation.formattedPrice, "$12.00") } @@ -177,7 +219,7 @@ final class PointOfSaleItemServiceTests: XCTestCase { network: network, isVariableProductsFeatureEnabled: true) let parentProductID: Int64 = 123 - let expectedError = PointOfSaleProductServiceError.requestFailed + let expectedError = PointOfSaleItemServiceError.requestFailed // When network.simulateError(requestUrlSuffix: "products/\(parentProductID)/variations", error: expectedError) @@ -190,13 +232,13 @@ final class PointOfSaleItemServiceTests: XCTestCase { name: "Tea", productImageSource: nil, productID: parentProductID, - type: .variable + type: .variable(.init(allAttributes: [])) ), pageNumber: 1 ) XCTFail("An error is expected.") } catch { - XCTAssertEqual(error as? PointOfSaleProductServiceError, expectedError) + XCTAssertEqual(error as? PointOfSaleItemServiceError, expectedError) } } }