diff --git a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetup.swift b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetup.swift index 39f3e9da729..5f6af304c92 100644 --- a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetup.swift +++ b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetup.swift @@ -18,6 +18,7 @@ struct PointOfSaleBarcodeScannerSetup: View { VStack { currentContent + .frame(maxWidth: .infinity, maxHeight: .infinity) Spacer() } .scrollVerticallyIfNeeded() diff --git a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupFlow.swift b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupFlow.swift index 9feb543c75d..9258559eac0 100644 --- a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupFlow.swift +++ b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupFlow.swift @@ -85,8 +85,33 @@ class PointOfSaleBarcodeScannerSetupFlow { ] case .starBSH20B: return [ - createWelcomeStep(title: "Star BSH-20B Setup") - // TODO: Add more steps for Star BSH-20B WOOMOB-696 + PointOfSaleBarcodeScannerSetupStep(content: { + PointOfSaleBarcodeScannerBarcodeView( + title: String(format: Localization.starSetUpBarcodeStepTitleFormat, scannerType.name), + instruction: Localization.setUpBarcodeStepInstruction, + barcode: .starBsh20SetupBarcode) + }), + PointOfSaleBarcodeScannerSetupStep(content: { + PointOfSaleBarcodeScannerPairingView(scanner: scannerType) + }), + PointOfSaleBarcodeScannerSetupStep( + content: { + PointOfSaleBarcodeScannerTestBarcodeView( + scanTester: PointOfSaleBarcodeScannerSetupScanTester( + onTestPass: { [weak self] in + self?.nextStep() + }, + onTestFailure: {}, + barcodeDefinition: .ean13) + ) + }, + buttonCustomization: PointOfSaleBarcodeScannerBackOnlyButtonCustomization() + ), + PointOfSaleBarcodeScannerSetupStep( + content: { + PointOfSaleBarcodeScannerSetupCompleteView() + }) + // TODO: Add optional error step and documentation step for Star BSH-20B WOOMOB-696 ] case .tbcScanner: return [ @@ -112,24 +137,23 @@ class PointOfSaleBarcodeScannerSetupFlow { } } -// MARK: - Button Customizations @available(iOS 17.0, *) -struct PointOfSaleBarcodeScannerWelcomeButtonCustomization: PointOfSaleBarcodeScannerButtonCustomization { +struct PointOfSaleBarcodeScannerBackOnlyButtonCustomization: PointOfSaleBarcodeScannerButtonCustomization { func customizeButtons(for flow: PointOfSaleBarcodeScannerSetupFlow) -> PointOfSaleFlowButtonConfiguration { return PointOfSaleFlowButtonConfiguration( - primaryButton: PointOfSaleFlowButtonConfiguration.ButtonConfig( - title: Localization.doneButtonTitle, - action: { flow.nextStep() } - ), - secondaryButton: nil + primaryButton: nil, + secondaryButton: PointOfSaleFlowButtonConfiguration.ButtonConfig( + title: Localization.backButtonTitle, + action: { flow.previousStep() } + ) ) } private enum Localization { - static let doneButtonTitle = NSLocalizedString( - "pos.barcodeScannerSetup.done.button.title", - value: "Done", - comment: "Title for the done button in barcode scanner setup navigation" + static let backButtonTitle = NSLocalizedString( + "pos.barcodeScannerSetup.back.button.title", + value: "Back", + comment: "Title for the back button in barcode scanner setup navigation" ) } } @@ -153,5 +177,8 @@ private extension PointOfSaleBarcodeScannerSetupFlow { value: "Back", comment: "Title for the back button in barcode scanner setup navigation" ) + //TODO: WOOMOB-792 + static let starSetUpBarcodeStepTitleFormat = "%1$@ Setup" + static let setUpBarcodeStepInstruction = "Scan the barcode to set up your scanner." } } diff --git a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupModels.swift b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupModels.swift index 12a1ce4554d..c0732ce9e9a 100644 --- a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupModels.swift +++ b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupModels.swift @@ -13,6 +13,29 @@ enum PointOfSaleBarcodeScannerType { case starBSH20B case tbcScanner case other + + var name: String { + switch self { + case .socketS720: + return Localization.socketS720Name + case .starBSH20B: + return Localization.starBsh20BName + case .tbcScanner: + return Localization.tbcScannerName + case .other: + return Localization.otherName + } + } +} + +private extension PointOfSaleBarcodeScannerType { + //TODO: WOOMOB-792 + enum Localization { + static let socketS720Name = "Socket S720" + static let starBsh20BName = "Star BSH-20B" + static let tbcScannerName = "TBC scanner" + static let otherName = "Other scanner" + } } // MARK: - Flow State @@ -35,7 +58,7 @@ struct PointOfSaleBarcodeScannerSetupStep { let buttonCustomization: PointOfSaleBarcodeScannerButtonCustomization? init( - title: String, + title: String = "", @ViewBuilder content: () -> any View, buttonCustomization: PointOfSaleBarcodeScannerButtonCustomization? = nil ) { @@ -44,3 +67,22 @@ struct PointOfSaleBarcodeScannerSetupStep { self.buttonCustomization = buttonCustomization } } + +// MARK: - Test Barcodes +enum PointOfSaleBarcodeScannerTestBarcode { + case ean13 + + var barcodeAsset: PointOfSaleAssets { + switch self { + case .ean13: + return .testEan13Barcode + } + } + + var expectedValue: String { + switch self { + case .ean13: + return "1234567890128" + } + } +} diff --git a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupScanTester.swift b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupScanTester.swift new file mode 100644 index 00000000000..bc530c92ecf --- /dev/null +++ b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupScanTester.swift @@ -0,0 +1,26 @@ +import Foundation + +struct PointOfSaleBarcodeScannerSetupScanTester { + private let onTestPass: () -> Void + private let onTestFailure: () -> Void + private let barcodeDefinition: PointOfSaleBarcodeScannerTestBarcode + + init(onTestPass: @escaping () -> Void, onTestFailure: @escaping () -> Void, barcodeDefinition: PointOfSaleBarcodeScannerTestBarcode) { + self.onTestPass = onTestPass + self.onTestFailure = onTestFailure + self.barcodeDefinition = barcodeDefinition + } + + var barcode: PointOfSaleAssets { + barcodeDefinition.barcodeAsset + } + + func handleScan(_ scanResult: Result) { + switch scanResult { + case .success(barcodeDefinition.expectedValue): + onTestPass() + case .success, .failure: + onTestFailure() + } + } +} diff --git a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupStepViews.swift b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupStepViews.swift new file mode 100644 index 00000000000..458eb01dcea --- /dev/null +++ b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupStepViews.swift @@ -0,0 +1,196 @@ +import SwiftUI + +// TODO: Remove this view when all flows are complete +struct PointOfSaleBarcodeScannerWelcomeView: View { + let title: String + + var body: some View { + VStack(spacing: POSSpacing.medium) { + Text(title) + .font(.posBodyLargeBold) + .foregroundColor(.posOnSurface) + + Text("TODO: Implement \(title) setup flow") + .font(.posBodyMediumRegular()) + .foregroundColor(.posOnSurfaceVariantHighest) + .multilineTextAlignment(.center) + } + } +} + +struct PointOfSaleBarcodeScannerBarcodeView: View { + let title: String + let instruction: String + let barcode: PointOfSaleAssets + + var body: some View { + VStack(spacing: POSSpacing.xLarge) { + VStack(alignment: .center, spacing: POSSpacing.small) { + Text(title) + .font(.posHeadingBold) + .foregroundColor(.posOnSurface) + .accessibilityAddTraits(.isHeader) + + Text(instruction) + .font(.posBodyMediumRegular()) + .foregroundColor(.posOnSurfaceVariantHighest) + .multilineTextAlignment(.center) + } + + Image(barcode.imageName) + } + } +} + +struct PointOfSaleBarcodeScannerPairingView: View { + let scanner: PointOfSaleBarcodeScannerType + + var body: some View { + VStack(spacing: POSSpacing.xLarge) { + // Temporary image until finalised assets are available + Image(systemName: "gearshape") + .font(.system(size: 78)) + .accessibilityHidden(true) + + VStack(alignment: .center, spacing: POSSpacing.small) { + Text(Localization.title) + .font(.posHeadingBold) + .foregroundColor(.posOnSurface) + .accessibilityAddTraits(.isHeader) + + Text(instruction) + .font(.posBodyMediumRegular()) + .foregroundColor(.posOnSurfaceVariantHighest) + .multilineTextAlignment(.center) + } + + Button { + guard let targetURL = URL(string: UIApplication.openSettingsURLString) else { + return + } + UIApplication.shared.open(targetURL) + } label: { + Text(Localization.settingsButtonTitle) + } + .buttonStyle(POSOutlinedButtonStyle(size: .extraSmall)) + } + } + + private var instruction: String { + String(format: Localization.instructionFormat, scanner.name) + } +} + +private extension PointOfSaleBarcodeScannerPairingView { + //TODO: WOOMOB-792 + enum Localization { + static let settingsButtonTitle = "Go to settings" + static let title = "Pair your device" + static let instructionFormat = "Enable Bluetooth and select your %1$@ scanner in iOS Settings." + } +} + +@available(iOS 17.0, *) +struct PointOfSaleBarcodeScannerTestBarcodeView: View { + let scanTester: PointOfSaleBarcodeScannerSetupScanTester + @State private var timerCompleted = false + @State private var timer: Timer? + + var body: some View { + PointOfSaleBarcodeScannerBarcodeView(title: timerCompleted ? Localization.timeoutTitle : Localization.title, + instruction: timerCompleted ? Localization.timeoutInstruction : Localization.instruction, + barcode: scanTester.barcode) + .barcodeScanning { result in + scanTester.handleScan(result) + } + .onAppear { + startTimer() + } + .onDisappear { + timer?.invalidate() + timer = nil + } + } + + private func startTimer() { + timer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false) { _ in + timerCompleted = true + } + } +} + +@available(iOS 17.0, *) +private extension PointOfSaleBarcodeScannerTestBarcodeView { + enum Localization { + static let title = "Test your scanner" + static let instruction = "Scan the barcode to test your scanner" + static let timeoutTitle = "No scan data found yet" + static let timeoutInstruction = "Scan the barcode to test your scanner. If the issue continues, please check Bluetooth settings and try again." + } +} + +struct PointOfSaleBarcodeScannerSetupCompleteView: View { + var body: some View { + VStack(spacing: POSSpacing.xLarge) { + // Temporary image until finalised assets are available + successIcon + .accessibilityHidden(true) + + VStack(alignment: .center, spacing: POSSpacing.small) { + Text(Localization.title) + .font(.posHeadingBold) + .foregroundColor(.posOnSurface) + .accessibilityAddTraits(.isHeader) + + Text(Localization.instruction) + .font(.posBodyMediumRegular()) + .foregroundColor(.posOnSurfaceVariantHighest) + .multilineTextAlignment(.center) + } + } + } + + @ViewBuilder private var successIcon: some View { + ZStack { + Circle() + .frame(width: 104, height: 104) + .foregroundColor(.posSuccess) + Image(PointOfSaleAssets.successCheck.imageName) + .renderingMode(.template) + .resizable() + .frame(width: 48, height: 48) + .foregroundColor(.posOnSuccess) + } + } +} + +private extension PointOfSaleBarcodeScannerSetupCompleteView { + enum Localization { + //TODO: WOOMOB-792 + static let title = "Scanner set up!" + static let instruction = "You are ready to start scanning products. \n" + + "Read more about barcode and QR code scanner support." + } +} + +// MARK: - Button Customizations +@available(iOS 17.0, *) +struct PointOfSaleBarcodeScannerWelcomeButtonCustomization: PointOfSaleBarcodeScannerButtonCustomization { + func customizeButtons(for flow: PointOfSaleBarcodeScannerSetupFlow) -> PointOfSaleFlowButtonConfiguration { + return PointOfSaleFlowButtonConfiguration( + primaryButton: PointOfSaleFlowButtonConfiguration.ButtonConfig( + title: Localization.doneButtonTitle, + action: { flow.nextStep() } + ), + secondaryButton: nil + ) + } + + private enum Localization { + static let doneButtonTitle = NSLocalizedString( + "pos.barcodeScannerSetup.done.button.title", + value: "Done", + comment: "Title for the done button in barcode scanner setup navigation" + ) + } +} diff --git a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupViews.swift b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupViews.swift index df479b348d1..d6163c10929 100644 --- a/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupViews.swift +++ b/WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupViews.swift @@ -56,25 +56,6 @@ struct PointOfSaleBarcodeScannerOptionView: View { } } -// MARK: - Step Views -struct PointOfSaleBarcodeScannerWelcomeView: View { - let title: String - - var body: some View { - VStack(spacing: POSSpacing.medium) { - Text(title) - .font(.posBodyLargeBold) - .foregroundColor(.posOnSurface) - - Text("TODO: Implement \(title) setup flow") - .font(.posBodyMediumRegular()) - .foregroundColor(.posOnSurfaceVariantHighest) - .multilineTextAlignment(.center) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - } -} - // MARK: - Private Localization Extensions private extension PointOfSaleBarcodeScannerSetupSelectionView { enum Localization { diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift index c297644add8..bd52bfcb9f9 100644 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift +++ b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift @@ -101,7 +101,7 @@ struct PointOfSalePaymentSuccessView: View { Image(PointOfSaleAssets.successCheck.imageName) .renderingMode(.template) .foregroundColor(checkmarkColor) - .frame(width: 52) + .frame(width: Constants.checkmarkSize) .accessibilityHidden(true) } } @@ -116,7 +116,7 @@ private extension PointOfSalePaymentSuccessView { enum Constants { static let imageName: String = "checkmark" static let imageSize: CGSize = .init(width: 165, height: 165) - static let checkmarkSize: CGFloat = 56 + static let checkmarkSize: CGFloat = 52 static let shadowOpacity: CGFloat = 0.16 static let shadowRadius: CGFloat = 16 static let shadowSize: CGSize = .init(width: 0, height: 8) diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleAssets.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleAssets.swift index 3bd6597d82c..855e8d175f0 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleAssets.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleAssets.swift @@ -14,6 +14,9 @@ enum PointOfSaleAssets: CaseIterable { case shoppingBags case successCheck case coupons + //TODO: WOOMOB-793 Update the imagesets for these barcodes to vector/dark mode friendly images + case starBsh20SetupBarcode + case testEan13Barcode var imageName: String { switch self { @@ -43,6 +46,10 @@ enum PointOfSaleAssets: CaseIterable { "pos-success-check" case .coupons: "coupons" + case .starBsh20SetupBarcode: + "star-bsh20-setup-barcode" + case .testEan13Barcode: + "test-ean13-barcode" } } } diff --git a/WooCommerce/Resources/Images.xcassets/POS/star-bsh20-setup-barcode.imageset/Contents.json b/WooCommerce/Resources/Images.xcassets/POS/star-bsh20-setup-barcode.imageset/Contents.json new file mode 100644 index 00000000000..6397e2d72f5 --- /dev/null +++ b/WooCommerce/Resources/Images.xcassets/POS/star-bsh20-setup-barcode.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "star-bsh20-setup-barcode.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WooCommerce/Resources/Images.xcassets/POS/star-bsh20-setup-barcode.imageset/star-bsh20-setup-barcode.pdf b/WooCommerce/Resources/Images.xcassets/POS/star-bsh20-setup-barcode.imageset/star-bsh20-setup-barcode.pdf new file mode 100644 index 00000000000..8b367953fd3 Binary files /dev/null and b/WooCommerce/Resources/Images.xcassets/POS/star-bsh20-setup-barcode.imageset/star-bsh20-setup-barcode.pdf differ diff --git a/WooCommerce/Resources/Images.xcassets/POS/test-ean13-barcode.imageset/Contents.json b/WooCommerce/Resources/Images.xcassets/POS/test-ean13-barcode.imageset/Contents.json new file mode 100644 index 00000000000..06f78194e6b --- /dev/null +++ b/WooCommerce/Resources/Images.xcassets/POS/test-ean13-barcode.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "barcode-1-2.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WooCommerce/Resources/Images.xcassets/POS/test-ean13-barcode.imageset/barcode-1-2.png b/WooCommerce/Resources/Images.xcassets/POS/test-ean13-barcode.imageset/barcode-1-2.png new file mode 100644 index 00000000000..eaf93522dcc Binary files /dev/null and b/WooCommerce/Resources/Images.xcassets/POS/test-ean13-barcode.imageset/barcode-1-2.png differ diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 4a85e1a4037..016248bc084 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -871,6 +871,8 @@ 207823E32C5D18CE00025A59 /* PointOfSaleCardPresentPaymentConnectionSuccessAlertViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 207823E22C5D18CE00025A59 /* PointOfSaleCardPresentPaymentConnectionSuccessAlertViewModel.swift */; }; 207823E52C5D1B2F00025A59 /* PointOfSaleCardPresentPaymentConnectionSuccessAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 207823E42C5D1B2F00025A59 /* PointOfSaleCardPresentPaymentConnectionSuccessAlertView.swift */; }; 207823E92C5D3A1700025A59 /* POSErrorExclamationMark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 207823E82C5D3A1700025A59 /* POSErrorExclamationMark.swift */; }; + 207CEA852E1FD59B0023EC35 /* PointOfSaleBarcodeScannerSetupScanTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 207CEA842E1FD59B0023EC35 /* PointOfSaleBarcodeScannerSetupScanTester.swift */; }; + 207CEA882E1FD6F80023EC35 /* PointOfSaleBarcodeScannerSetupScanTesterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 207CEA872E1FD6F80023EC35 /* PointOfSaleBarcodeScannerSetupScanTesterTests.swift */; }; 207D2D232CFDCCBF00F79204 /* MockPOSOrderableItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 207D2D222CFDCCBF00F79204 /* MockPOSOrderableItem.swift */; }; 207E71CB2C60F765008540FC /* MockPOSOrderService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 207E71CA2C60F765008540FC /* MockPOSOrderService.swift */; }; 2084B7A22C77693600EFBD2E /* CardPresentPaymentsModalButtonViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2084B7A12C77693600EFBD2E /* CardPresentPaymentsModalButtonViewModelTests.swift */; }; @@ -886,6 +888,7 @@ 2088784B2D96E98000F7AE03 /* PointOfSaleCardPresentPaymentConnectingLocationPreAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2088784A2D96E98000F7AE03 /* PointOfSaleCardPresentPaymentConnectingLocationPreAlertView.swift */; }; 2088784D2D96EA3900F7AE03 /* PointOfSaleCardPresentPaymentConnectingLocationPreAlertViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2088784C2D96EA3900F7AE03 /* PointOfSaleCardPresentPaymentConnectingLocationPreAlertViewModel.swift */; }; 20897C9E2D4A68C5008AD16C /* PointOfSaleUnsupportedWidthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20897C9D2D4A68C5008AD16C /* PointOfSaleUnsupportedWidthView.swift */; }; + 208C0F0A2E1FAC1900FE619E /* PointOfSaleBarcodeScannerSetupStepViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 208C0F092E1FAC1900FE619E /* PointOfSaleBarcodeScannerSetupStepViews.swift */; }; 209566252D4CF00100977124 /* PointOfSalePaymentMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209566242D4CF00100977124 /* PointOfSalePaymentMethod.swift */; }; 209AD3D02AC1EDDA00825D76 /* WooPaymentsPayoutsCurrencyOverviewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209AD3CF2AC1EDDA00825D76 /* WooPaymentsPayoutsCurrencyOverviewViewModel.swift */; }; 209AD3D22AC1EDF600825D76 /* WooPaymentsPayoutsCurrencyOverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209AD3D12AC1EDF600825D76 /* WooPaymentsPayoutsCurrencyOverviewView.swift */; }; @@ -4042,6 +4045,8 @@ 207823E22C5D18CE00025A59 /* PointOfSaleCardPresentPaymentConnectionSuccessAlertViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentConnectionSuccessAlertViewModel.swift; sourceTree = ""; }; 207823E42C5D1B2F00025A59 /* PointOfSaleCardPresentPaymentConnectionSuccessAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentConnectionSuccessAlertView.swift; sourceTree = ""; }; 207823E82C5D3A1700025A59 /* POSErrorExclamationMark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSErrorExclamationMark.swift; sourceTree = ""; }; + 207CEA842E1FD59B0023EC35 /* PointOfSaleBarcodeScannerSetupScanTester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleBarcodeScannerSetupScanTester.swift; sourceTree = ""; }; + 207CEA872E1FD6F80023EC35 /* PointOfSaleBarcodeScannerSetupScanTesterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleBarcodeScannerSetupScanTesterTests.swift; sourceTree = ""; }; 207D2D222CFDCCBF00F79204 /* MockPOSOrderableItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MockPOSOrderableItem.swift; path = ../Modules/Tests/YosemiteTests/Mocks/MockPOSOrderableItem.swift; sourceTree = SOURCE_ROOT; }; 207E71CA2C60F765008540FC /* MockPOSOrderService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPOSOrderService.swift; sourceTree = ""; }; 2084B7A12C77693600EFBD2E /* CardPresentPaymentsModalButtonViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentPaymentsModalButtonViewModelTests.swift; sourceTree = ""; }; @@ -4057,6 +4062,7 @@ 2088784A2D96E98000F7AE03 /* PointOfSaleCardPresentPaymentConnectingLocationPreAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentConnectingLocationPreAlertView.swift; sourceTree = ""; }; 2088784C2D96EA3900F7AE03 /* PointOfSaleCardPresentPaymentConnectingLocationPreAlertViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentConnectingLocationPreAlertViewModel.swift; sourceTree = ""; }; 20897C9D2D4A68C5008AD16C /* PointOfSaleUnsupportedWidthView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleUnsupportedWidthView.swift; sourceTree = ""; }; + 208C0F092E1FAC1900FE619E /* PointOfSaleBarcodeScannerSetupStepViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleBarcodeScannerSetupStepViews.swift; sourceTree = ""; }; 209566242D4CF00100977124 /* PointOfSalePaymentMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSalePaymentMethod.swift; sourceTree = ""; }; 209AD3CF2AC1EDDA00825D76 /* WooPaymentsPayoutsCurrencyOverviewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooPaymentsPayoutsCurrencyOverviewViewModel.swift; sourceTree = ""; }; 209AD3D12AC1EDF600825D76 /* WooPaymentsPayoutsCurrencyOverviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooPaymentsPayoutsCurrencyOverviewView.swift; sourceTree = ""; }; @@ -7227,6 +7233,7 @@ 026A502D2D2F8087002C42C2 /* Presentation */ = { isa = PBXGroup; children = ( + 207CEA862E1FD6D80023EC35 /* Barcode Scanner Setup */, 20D5575E2DFAE3D500D9EC8B /* Barcode Scanning */, 01AB2D132DDC7CD000AA67FD /* POSItemActionHandlerFactoryTests.swift */, 01AB2D112DDC7AD100AA67FD /* PointOfSaleItemListAnalyticsTrackerTests.swift */, @@ -8232,6 +8239,14 @@ path = "Reader Messages"; sourceTree = ""; }; + 207CEA862E1FD6D80023EC35 /* Barcode Scanner Setup */ = { + isa = PBXGroup; + children = ( + 207CEA872E1FD6F80023EC35 /* PointOfSaleBarcodeScannerSetupScanTesterTests.swift */, + ); + path = "Barcode Scanner Setup"; + sourceTree = ""; + }; 2084B7A02C7768E200EFBD2E /* Connection Alerts */ = { isa = PBXGroup; children = ( @@ -8310,6 +8325,8 @@ 20C3DB1F2E1E69CF00CF7D3B /* PointOfSaleBarcodeScannerSetupFlowManager.swift */, 20C3DB202E1E69CF00CF7D3B /* PointOfSaleBarcodeScannerSetupModels.swift */, 20C3DB212E1E69CF00CF7D3B /* PointOfSaleBarcodeScannerSetupViews.swift */, + 208C0F092E1FAC1900FE619E /* PointOfSaleBarcodeScannerSetupStepViews.swift */, + 207CEA842E1FD59B0023EC35 /* PointOfSaleBarcodeScannerSetupScanTester.swift */, 20C3DB282E1E6FBA00CF7D3B /* PointOfSaleFlowButtonsView.swift */, ); path = "Barcode Scanner Setup"; @@ -15239,6 +15256,7 @@ 03E471CA293E0A30001A58AD /* CardPresentModalTapToPayConfigurationProgress.swift in Sources */, 31AD0B1126E9575F000B6391 /* CardPresentModalConnectingFailed.swift in Sources */, 576EA39425264C9B00AFC0B3 /* RefundConfirmationViewModel.swift in Sources */, + 208C0F0A2E1FAC1900FE619E /* PointOfSaleBarcodeScannerSetupStepViews.swift in Sources */, 02ED3D272C23315400ED6F3E /* PointOfSaleCardPresentPaymentReaderUpdateFailedView.swift in Sources */, 01BD77482C58D19C00147191 /* PointOfSaleCardPresentPaymentCancelledOnReaderMessageView.swift in Sources */, DEC51B00276AEE91009F3DF4 /* SystemStatusReportViewModel.swift in Sources */, @@ -15453,6 +15471,7 @@ CE22E3F72170E23C005A6BEF /* PrivacySettingsViewController.swift in Sources */, 02222BD02D5AFE4F00FB97D2 /* POSButtonProgressViewStyle.swift in Sources */, 021125482577CC650075AD2A /* ShippingLabelDetailsViewModel.swift in Sources */, + 207CEA852E1FD59B0023EC35 /* PointOfSaleBarcodeScannerSetupScanTester.swift in Sources */, 2004E2D82C08E56300D62521 /* CardPresentPaymentOnboardingPresentationEvent.swift in Sources */, 68D1BEDD2900E4180074A29E /* CustomerSearchUICommand.swift in Sources */, 316837DA25CCA90C00E36B2F /* OrderStatusListDataSource.swift in Sources */, @@ -17594,6 +17613,7 @@ B9CB14E02A42E246005912C2 /* BarcodeScannerErrorNoticeFactoryTests.swift in Sources */, 6856D49DB7DCF4D87745C0B1 /* MockPushNotificationsManager.swift in Sources */, 4569D3C925DC065B00CDC3E2 /* SiteAddressTests.swift in Sources */, + 207CEA882E1FD6F80023EC35 /* PointOfSaleBarcodeScannerSetupScanTesterTests.swift in Sources */, EE289AFE2C9D9CF0004AB1A6 /* ProductCreationAIEligibilityCheckerTests.swift in Sources */, CC4D1E7925EE415D00B6E4E7 /* RenameAttributesViewModelTests.swift in Sources */, 458BAC702C57E38F009440EA /* ProductPasswordEligibilityUseCaseTests.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupScanTesterTests.swift b/WooCommerce/WooCommerceTests/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupScanTesterTests.swift new file mode 100644 index 00000000000..9e42ed01628 --- /dev/null +++ b/WooCommerce/WooCommerceTests/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupScanTesterTests.swift @@ -0,0 +1,80 @@ +import Testing +@testable import WooCommerce + +struct PointOfSaleBarcodeScannerSetupScanTesterTests { + + @Test func test_scanTester_calls_onTestPass_when_scan_received_for_expected_barcode() { + // Given a test EAN13 barcode + let expectedBarcode = PointOfSaleBarcodeScannerTestBarcode.ean13 + var onTestPassCalled = false + var onTestFailureCalled = false + + let sut = PointOfSaleBarcodeScannerSetupScanTester( + onTestPass: { onTestPassCalled = true }, + onTestFailure: { onTestFailureCalled = true }, + barcodeDefinition: expectedBarcode) + + // When the barcode is scanned + sut.handleScan(.success(expectedBarcode.expectedValue)) + + // Then it calls the pass closure + #expect(onTestPassCalled == true) + #expect(onTestFailureCalled == false) + } + + @Test func test_scanTester_calls_onTestFailure_when_scan_received_for_unexpected_barcode() { + // Given a test EAN13 barcode + let expectedBarcode = PointOfSaleBarcodeScannerTestBarcode.ean13 + var onTestPassCalled = false + var onTestFailureCalled = false + + let sut = PointOfSaleBarcodeScannerSetupScanTester( + onTestPass: { onTestPassCalled = true }, + onTestFailure: { onTestFailureCalled = true }, + barcodeDefinition: expectedBarcode) + + // When an unexpected barcode is scanned + sut.handleScan(.success("9999999999999")) + + // Then it calls the failure closure + #expect(onTestPassCalled == false) + #expect(onTestFailureCalled == true) + } + + @Test func test_scanTester_calls_onTestFailure_when_scan_fails() { + // Given a test EAN13 barcode + let expectedBarcode = PointOfSaleBarcodeScannerTestBarcode.ean13 + var onTestPassCalled = false + var onTestFailureCalled = false + + let sut = PointOfSaleBarcodeScannerSetupScanTester( + onTestPass: { onTestPassCalled = true }, + onTestFailure: { onTestFailureCalled = true }, + barcodeDefinition: expectedBarcode) + + // When the scan fails + sut.handleScan(.failure(TestError.scanFailed)) + + // Then it calls the failure closure + #expect(onTestPassCalled == false) + #expect(onTestFailureCalled == true) + } + + private enum TestError: Error { + case scanFailed + } + + @Test func test_scanTester_provides_correct_barcode_asset() { + // Given a test EAN13 barcode + let expectedBarcode = PointOfSaleBarcodeScannerTestBarcode.ean13 + + let sut = PointOfSaleBarcodeScannerSetupScanTester( + onTestPass: {}, + onTestFailure: {}, + barcodeDefinition: expectedBarcode) + + // Then it provides the correct barcode asset + #expect(sut.barcode == .testEan13Barcode) + } + +}