Skip to content

Commit e7defd8

Browse files
committed
Merge upstream changes from develop into feature/4846-variation-number-in-list-and-details
2 parents e80525b + 0c7caeb commit e7defd8

File tree

95 files changed

+3692
-926
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+3692
-926
lines changed

Experiments/Experiments/DefaultFeatureFlagService.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
2727
return buildConfig == .localDeveloper || buildConfig == .alpha
2828
case .jetpackConnectionPackageSupport:
2929
return buildConfig == .localDeveloper || buildConfig == .alpha
30+
case .orderCreation:
31+
return buildConfig == .localDeveloper || buildConfig == .alpha
3032
default:
3133
return true
3234
}

Experiments/Experiments/FeatureFlag.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@ public enum FeatureFlag: Int {
5353
/// Allows sites with plugins that include Jetpack Connection Package and without Jetpack-the-plugin to connect to the app
5454
///
5555
case jetpackConnectionPackageSupport
56+
57+
/// Allows new orders to be manually created
58+
///
59+
case orderCreation
5660
}

Fakes/Fakes/Networking.generated.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,8 @@ extension Site {
13141314
description: .fake(),
13151315
url: .fake(),
13161316
plan: .fake(),
1317+
isJetpackThePluginInstalled: .fake(),
1318+
isJetpackConnected: .fake(),
13171319
isWooCommerceActive: .fake(),
13181320
isWordPressStore: .fake(),
13191321
timezone: .fake(),

Hardware/Hardware.xcodeproj/project.pbxproj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/* Begin PBXBuildFile section */
1010
030338102705F7D400764131 /* ReceiptTotalLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0303380F2705F7D400764131 /* ReceiptTotalLine.swift */; };
1111
311889EB2653286B0080AEA2 /* PaymentIntentMetadataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 311889EA2653286B0080AEA2 /* PaymentIntentMetadataTests.swift */; };
12+
55CD4BB4273E617C007686D3 /* ReceiptRendererTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55CD4BB3273E617C007686D3 /* ReceiptRendererTest.swift */; };
1213
5A747BE9FA06EC8752A35752 /* Pods_HardwareTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1DC5B6141B8184FAC29B0A4 /* Pods_HardwareTests.framework */; };
1314
8FFAA245E257B9EB98E2FCBD /* Pods_SampleReceiptPrinter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AFA997D6786C67B0A061854 /* Pods_SampleReceiptPrinter.framework */; };
1415
C5D2CB7D21CEE28FEBF18BF6 /* Pods_Hardware.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9F0AC202B287C1221EA2C99 /* Pods_Hardware.framework */; };
@@ -134,6 +135,7 @@
134135
311889EA2653286B0080AEA2 /* PaymentIntentMetadataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentIntentMetadataTests.swift; sourceTree = "<group>"; };
135136
3CF9348BADD5E0518080A61A /* Pods-Hardware.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Hardware.debug.xcconfig"; path = "Target Support Files/Pods-Hardware/Pods-Hardware.debug.xcconfig"; sourceTree = "<group>"; };
136137
47BDD50287B7B0CF8D769BFC /* Pods-PrinterPlayground.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PrinterPlayground.release-alpha.xcconfig"; path = "Target Support Files/Pods-PrinterPlayground/Pods-PrinterPlayground.release-alpha.xcconfig"; sourceTree = "<group>"; };
138+
55CD4BB3273E617C007686D3 /* ReceiptRendererTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptRendererTest.swift; sourceTree = "<group>"; };
137139
9726331F55A9621F2F887E13 /* Pods-Hardware.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Hardware.release.xcconfig"; path = "Target Support Files/Pods-Hardware/Pods-Hardware.release.xcconfig"; sourceTree = "<group>"; };
138140
AE60F16D43C20AD4523A61A5 /* Pods-SampleReceiptPrinter.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleReceiptPrinter.debug.xcconfig"; path = "Target Support Files/Pods-SampleReceiptPrinter/Pods-SampleReceiptPrinter.debug.xcconfig"; sourceTree = "<group>"; };
139141
B1DC5B6141B8184FAC29B0A4 /* Pods_HardwareTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HardwareTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -258,6 +260,14 @@
258260
/* End PBXFrameworksBuildPhase section */
259261

260262
/* Begin PBXGroup section */
263+
55CD4BB2273E6159007686D3 /* AirPrintReceipt */ = {
264+
isa = PBXGroup;
265+
children = (
266+
55CD4BB3273E617C007686D3 /* ReceiptRendererTest.swift */,
267+
);
268+
path = AirPrintReceipt;
269+
sourceTree = "<group>";
270+
};
261271
ADED522B787D093F499BD540 /* Pods */ = {
262272
isa = PBXGroup;
263273
children = (
@@ -351,6 +361,7 @@
351361
D88FDB0C25DD216B00CB0DBD /* HardwareTests */ = {
352362
isa = PBXGroup;
353363
children = (
364+
55CD4BB2273E6159007686D3 /* AirPrintReceipt */,
354365
D845BDF3262DD65D00A3E40F /* Mocks */,
355366
D88FDB0F25DD216B00CB0DBD /* Info.plist */,
356367
D892F21325E3F67A001D7134 /* CardReaderTests.swift */,
@@ -566,6 +577,7 @@
566577
};
567578
D88FDB0725DD216B00CB0DBD = {
568579
CreatedOnToolsVersion = 12.4;
580+
LastSwiftMigration = 1310;
569581
};
570582
E1CFC14A2643E9EE0089F86F = {
571583
CreatedOnToolsVersion = 12.4;
@@ -797,6 +809,7 @@
797809
D81AE85E25E6A89F00D9CFD3 /* StripeCardReaderCacheTests.swift in Sources */,
798810
D892F21425E3F67A001D7134 /* CardReaderTests.swift in Sources */,
799811
D854FC26260A6B5800A219CD /* ErrorCodesTests.swift in Sources */,
812+
55CD4BB4273E617C007686D3 /* ReceiptRendererTest.swift in Sources */,
800813
D892F21025E3F4C0001D7134 /* MockStripeCardReader.swift in Sources */,
801814
D88303DB25E450E700C877F9 /* PaymentIntentTests.swift in Sources */,
802815
D845BE03262DDBF500A3E40F /* CardBrandTests.swift in Sources */,
@@ -1019,6 +1032,7 @@
10191032
baseConfigurationReference = 1CC96A71F2937BCB7432766F /* Pods-HardwareTests.debug.xcconfig */;
10201033
buildSettings = {
10211034
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "${inherited}";
1035+
CLANG_ENABLE_MODULES = YES;
10221036
CODE_SIGN_IDENTITY = "iPhone Developer";
10231037
CODE_SIGN_STYLE = Automatic;
10241038
DEVELOPMENT_TEAM = PZYM8XX95Q;
@@ -1030,6 +1044,7 @@
10301044
);
10311045
PRODUCT_BUNDLE_IDENTIFIER = com.automattic.Hardware.HardwareTests;
10321046
PRODUCT_NAME = "$(TARGET_NAME)";
1047+
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
10331048
SWIFT_VERSION = 5.0;
10341049
TARGETED_DEVICE_FAMILY = "1,2";
10351050
};
@@ -1040,6 +1055,7 @@
10401055
baseConfigurationReference = C61D1642BE09D1A1AD6AA9FA /* Pods-HardwareTests.release.xcconfig */;
10411056
buildSettings = {
10421057
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "${inherited}";
1058+
CLANG_ENABLE_MODULES = YES;
10431059
CODE_SIGN_IDENTITY = "iPhone Developer";
10441060
CODE_SIGN_STYLE = Automatic;
10451061
DEVELOPMENT_TEAM = PZYM8XX95Q;
@@ -1147,6 +1163,7 @@
11471163
baseConfigurationReference = DB2B86965868C0EC25910D7D /* Pods-HardwareTests.release-alpha.xcconfig */;
11481164
buildSettings = {
11491165
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "${inherited}";
1166+
CLANG_ENABLE_MODULES = YES;
11501167
CODE_SIGN_IDENTITY = "iPhone Developer";
11511168
CODE_SIGN_STYLE = Automatic;
11521169
DEVELOPMENT_TEAM = PZYM8XX95Q;

Hardware/Hardware/CardReader/CardReaderConfigProvider.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public protocol ReaderLocationProvider {
1010
func fetchDefaultLocationID(completion: @escaping(String?, Error?) -> Void)
1111
}
1212

13-
public protocol CardReaderConfigProvider: ReaderLocationProvider {
13+
public protocol CardReaderConfigProvider: ReaderLocationProvider, ReaderTokenProvider {
1414
func fetchToken(completion: @escaping(String?, Error?) -> Void)
1515
func fetchDefaultLocationID(completion: @escaping(String?, Error?) -> Void)
1616
}

Hardware/Hardware/CardReader/StripeCardReader/DefaultConnectionTokenProvider.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import StripeTerminal
44
/// uses the networking adapter provided by clients of Hardware
55
/// to fetch a connection token
66
final class DefaultConnectionTokenProvider: ConnectionTokenProvider {
7-
private let provider: CardReaderConfigProvider
7+
private let provider: ReaderTokenProvider
88

9-
init(provider: CardReaderConfigProvider) {
9+
init(provider: ReaderTokenProvider) {
1010
self.provider = provider
1111
}
1212

Hardware/Hardware/Printer/AirPrintReceipt/ReceiptRenderer.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ private extension ReceiptRenderer {
110110
private func summaryTable() -> String {
111111
var summaryContent = "<table>"
112112
for line in content.lineItems {
113-
summaryContent += "<tr><td>\(line.title) × \(line.quantity)</td><td>\(line.amount) \(content.parameters.currency.uppercased())</td></tr>"
113+
let stripedTitle = line.title.htmlStripped()
114+
summaryContent += "<tr><td>\(stripedTitle) × \(line.quantity)</td><td>\(line.amount) \(content.parameters.currency.uppercased())</td></tr>"
114115
}
115116
summaryContent += totalRows()
116117
summaryContent += "</table>"
@@ -158,9 +159,9 @@ private extension ReceiptRenderer {
158159
/// are required in the US.
159160
/// https://stripe.com/docs/terminal/checkout/receipts#custom
160161
return """
161-
\(Localization.applicationName): \(emv.applicationPreferredName)<br/>
162-
\(Localization.aid): \(emv.dedicatedFileName)
163-
"""
162+
\(Localization.applicationName): \(emv.applicationPreferredName.htmlStripped())<br/>
163+
\(Localization.aid): \(emv.dedicatedFileName.htmlStripped())
164+
"""
164165
}
165166

166167
private func cardIconCSS() -> String {
@@ -250,3 +251,19 @@ private extension ReceiptRenderer {
250251
)
251252
}
252253
}
254+
255+
private extension String {
256+
func htmlStripped() -> String {
257+
let data = Data(utf8)
258+
do {
259+
return try NSAttributedString(
260+
data: data,
261+
options: [.documentType: NSAttributedString.DocumentType.html],
262+
documentAttributes: nil
263+
).string
264+
} catch {
265+
DDLogError("Failed to remove HTML from \(self): \(error.localizedDescription)")
266+
return self
267+
}
268+
}
269+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import XCTest
2+
@testable import Hardware
3+
import StripeTerminal
4+
import Foundation
5+
import CryptoKit
6+
7+
final class ReceiptRendererTest: XCTestCase {
8+
func test_TextWithoutHtmlSymbols() {
9+
let expectedResultWithoutHtmlSymbolsMd5Description = "MD5 digest: 532a92e85527596a692b4b9ee7c978a5"
10+
let content = generateReceiptContent()
11+
12+
let renderer = ReceiptRenderer(content: content)
13+
14+
XCTAssertEqual(
15+
Insecure.MD5.hash(data: renderer.htmlContent().data(using: .utf8)!).description,
16+
expectedResultWithoutHtmlSymbolsMd5Description
17+
)
18+
}
19+
20+
func test_TextWithHtmlSymbols() {
21+
let expectedResultWithHtmlSymbolsMd5Description = "MD5 digest: 034ae681824b18c473c9ca9ad69a06bb"
22+
let stringWithHtml = "<tt><table></table></footer>"
23+
let content = generateReceiptContent(stringToAppend: stringWithHtml)
24+
25+
let renderer = ReceiptRenderer(content: content)
26+
27+
print(renderer.htmlContent())
28+
29+
XCTAssertEqual(
30+
Insecure.MD5.hash(data: renderer.htmlContent().data(using: .utf8)!).description,
31+
expectedResultWithHtmlSymbolsMd5Description
32+
)
33+
}
34+
}
35+
36+
private extension ReceiptRendererTest {
37+
func generateReceiptContent(stringToAppend: String = "") -> ReceiptContent {
38+
ReceiptContent(
39+
parameters: CardPresentReceiptParameters(
40+
amount: 1,
41+
formattedAmount: "1",
42+
currency: "USD",
43+
date: .init(timeIntervalSince1970: 1636970486),
44+
storeName: "Test Store",
45+
cardDetails: .init(
46+
last4: "1234",
47+
expMonth: 12,
48+
expYear: 26,
49+
cardholderName: "John Smith",
50+
brand: .masterCard,
51+
fingerprint: "fpr*****",
52+
generatedCard: "pm_******",
53+
receipt: .init(
54+
applicationPreferredName: "Stripe Credit\(stringToAppend)",
55+
dedicatedFileName: "A00000000000000\(stringToAppend)",
56+
authorizationResponseCode: "0000",
57+
applicationCryptogram: "XXXXXXXXXXXX",
58+
terminalVerificationResults: "101010101010101010",
59+
transactionStatusInformation: "6800",
60+
accountType: "credit"
61+
),
62+
emvAuthData: "AD*******"),
63+
orderID: 9201
64+
),
65+
lineItems: [ReceiptLineItem(title: "Sample product #1\(stringToAppend)", quantity: "2", amount: "25")],
66+
cartTotals: [ReceiptTotalLine(description: "description", amount: "13")],
67+
orderNote: nil
68+
)
69+
}
70+
}

Networking/Networking/Model/Copiable/Models+Copiable.generated.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,8 @@ extension Site {
11511151
description: CopiableProp<String> = .copy,
11521152
url: CopiableProp<String> = .copy,
11531153
plan: CopiableProp<String> = .copy,
1154+
isJetpackThePluginInstalled: CopiableProp<Bool> = .copy,
1155+
isJetpackConnected: CopiableProp<Bool> = .copy,
11541156
isWooCommerceActive: CopiableProp<Bool> = .copy,
11551157
isWordPressStore: CopiableProp<Bool> = .copy,
11561158
timezone: CopiableProp<String> = .copy,
@@ -1161,6 +1163,8 @@ extension Site {
11611163
let description = description ?? self.description
11621164
let url = url ?? self.url
11631165
let plan = plan ?? self.plan
1166+
let isJetpackThePluginInstalled = isJetpackThePluginInstalled ?? self.isJetpackThePluginInstalled
1167+
let isJetpackConnected = isJetpackConnected ?? self.isJetpackConnected
11641168
let isWooCommerceActive = isWooCommerceActive ?? self.isWooCommerceActive
11651169
let isWordPressStore = isWordPressStore ?? self.isWordPressStore
11661170
let timezone = timezone ?? self.timezone
@@ -1172,6 +1176,8 @@ extension Site {
11721176
description: description,
11731177
url: url,
11741178
plan: plan,
1179+
isJetpackThePluginInstalled: isJetpackThePluginInstalled,
1180+
isJetpackConnected: isJetpackConnected,
11751181
isWooCommerceActive: isWooCommerceActive,
11761182
isWordPressStore: isWordPressStore,
11771183
timezone: timezone,

Networking/Networking/Model/Site.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ public struct Site: Decodable, Equatable, GeneratedFakeable, GeneratedCopiable {
2525
///
2626
public let plan: String
2727

28+
/// Whether the site has Jetpack-the-plugin installed.
29+
///
30+
public let isJetpackThePluginInstalled: Bool
31+
32+
/// Whether the site is connected to Jetpack, either through Jetpack-the-plugin or other plugins that include Jetpack Connection Package.
33+
///
34+
public let isJetpackConnected: Bool
35+
2836
/// Indicates if there is a WooCommerce Store Active.
2937
///
3038
public let isWooCommerceActive: Bool
@@ -50,6 +58,8 @@ public struct Site: Decodable, Equatable, GeneratedFakeable, GeneratedCopiable {
5058
let name = try siteContainer.decode(String.self, forKey: .name)
5159
let description = try siteContainer.decode(String.self, forKey: .description)
5260
let url = try siteContainer.decode(String.self, forKey: .url)
61+
let isJetpackThePluginInstalled = try siteContainer.decode(Bool.self, forKey: .isJetpackThePluginInstalled)
62+
let isJetpackConnected = try siteContainer.decode(Bool.self, forKey: .isJetpackConnected)
5363

5464
let optionsContainer = try siteContainer.nestedContainer(keyedBy: OptionKeys.self, forKey: .options)
5565
let isWordPressStore = try optionsContainer.decode(Bool.self, forKey: .isWordPressStore)
@@ -62,6 +72,8 @@ public struct Site: Decodable, Equatable, GeneratedFakeable, GeneratedCopiable {
6272
description: description,
6373
url: url,
6474
plan: String(), // Not created on init. Added in supplementary API request.
75+
isJetpackThePluginInstalled: isJetpackThePluginInstalled,
76+
isJetpackConnected: isJetpackConnected,
6577
isWooCommerceActive: isWooCommerceActive,
6678
isWordPressStore: isWordPressStore,
6779
timezone: timezone,
@@ -75,6 +87,8 @@ public struct Site: Decodable, Equatable, GeneratedFakeable, GeneratedCopiable {
7587
description: String,
7688
url: String,
7789
plan: String,
90+
isJetpackThePluginInstalled: Bool,
91+
isJetpackConnected: Bool,
7892
isWooCommerceActive: Bool,
7993
isWordPressStore: Bool,
8094
timezone: String,
@@ -84,13 +98,22 @@ public struct Site: Decodable, Equatable, GeneratedFakeable, GeneratedCopiable {
8498
self.description = description
8599
self.url = url
86100
self.plan = plan
101+
self.isJetpackThePluginInstalled = isJetpackThePluginInstalled
102+
self.isJetpackConnected = isJetpackConnected
87103
self.isWordPressStore = isWordPressStore
88104
self.isWooCommerceActive = isWooCommerceActive
89105
self.timezone = timezone
90106
self.gmtOffset = gmtOffset
91107
}
92108
}
93109

110+
public extension Site {
111+
/// Whether the site is connected to Jetpack with Jetpack Connection Package, and not with Jetpack-the-plugin.
112+
///
113+
var isJetpackCPConnected: Bool {
114+
isJetpackConnected && !isJetpackThePluginInstalled
115+
}
116+
}
94117

95118
/// Defines all of the Site CodingKeys.
96119
///
@@ -103,6 +126,8 @@ private extension Site {
103126
case url = "URL"
104127
case options = "options"
105128
case plan = "plan"
129+
case isJetpackThePluginInstalled = "jetpack"
130+
case isJetpackConnected = "jetpack_connection"
106131
}
107132

108133
enum OptionKeys: String, CodingKey {

0 commit comments

Comments
 (0)