diff --git a/WordPress/Classes/ViewRelated/NUX/SignupEpilogueTableViewController.swift b/WordPress/Classes/ViewRelated/NUX/SignupEpilogueTableViewController.swift index edc1e11c574b..9a16b374cfb8 100644 --- a/WordPress/Classes/ViewRelated/NUX/SignupEpilogueTableViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/SignupEpilogueTableViewController.swift @@ -183,7 +183,7 @@ private extension SignupEpilogueTableViewController { if let customDisplayName = dataSource?.customDisplayName { userInfo.fullName = customDisplayName } else { - let autoDisplayName = generateDisplayName(from: userInfo.email) + let autoDisplayName = Self.generateDisplayName(from: userInfo.email) userInfo.fullName = autoDisplayName delegate?.displayNameAutoGenerated(newDisplayName: autoDisplayName) } @@ -191,21 +191,6 @@ private extension SignupEpilogueTableViewController { epilogueUserInfo = userInfo } - func generateDisplayName(from rawEmail: String) -> String { - // step 1: lower case - let email = rawEmail.lowercased() - // step 2: remove the @ and everything after - let localPart = email.split(separator: "@")[0] - // step 3: remove all non-alpha characters - let localCleaned = localPart.replacingOccurrences(of: "[^A-Za-z/.]", with: "", options: .regularExpression) - // step 4: turn periods into spaces - let nameLowercased = localCleaned.replacingOccurrences(of: ".", with: " ") - // step 5: capitalize - let autoDisplayName = nameLowercased.capitalized - - return autoDisplayName - } - func getEpilogueCellFor(cellType: EpilogueCellType) -> SignupEpilogueCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.signupEpilogueCell) as? SignupEpilogueCell else { return SignupEpilogueCell() @@ -282,3 +267,33 @@ extension SignupEpilogueTableViewController: SignupEpilogueCellDelegate { } } + +extension SignupEpilogueTableViewController { + + // Notice that this duplicates almost one-to-one the logic from + // `ZendeskUtils.generateDisplayName(from:)` with the only difference being the method on + // `ZendeskUtils` returns `nil` if there is no "@" in the input. + // + // Later down the track, we might want to merge the two, ideally by updating this code to + // handle a `String?` value. Alternativetly, we could define an `Email` `String` wrapper and + // push the responsibility to validate the input as an email up the chain. + // + // At the time of writing, it was better to ensure the code didn't crash rather than + // restructuring the callsite. + // + // See https://github.com/wordpress-mobile/WordPressAuthenticator-iOS/issues/759 + static func generateDisplayName(from rawEmail: String) -> String { + // step 1: lower case + let email = rawEmail.lowercased() + // step 2: remove the @ and everything after + let localPart = email.split(separator: "@")[0] + // step 3: remove all non-alpha characters + let localCleaned = localPart.replacingOccurrences(of: "[^A-Za-z/.]", with: "", options: .regularExpression) + // step 4: turn periods into spaces + let nameLowercased = localCleaned.replacingOccurrences(of: ".", with: " ") + // step 5: capitalize + let autoDisplayName = nameLowercased.capitalized + + return autoDisplayName + } +} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index fbddc95d8639..d7e38ea324d8 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -787,6 +787,7 @@ 3F8513DF260D091500A4B938 /* RoundRectangleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8513DE260D091500A4B938 /* RoundRectangleView.swift */; }; 3F851415260D0A3300A4B938 /* UnifiedPrologueEditorContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F851414260D0A3300A4B938 /* UnifiedPrologueEditorContentView.swift */; }; 3F851428260D1EA300A4B938 /* CircledIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F851427260D1EA300A4B938 /* CircledIcon.swift */; }; + 3F86A83729D19C15005D20C0 /* SignupEpilogueTableViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F86A83629D19C15005D20C0 /* SignupEpilogueTableViewControllerTests.swift */; }; 3F88065B26C30F2A0074DD21 /* TimeSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F88065A26C30F2A0074DD21 /* TimeSelectionViewController.swift */; }; 3F8A087D253E4337000F35ED /* ColorPalette.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 435B76292297484200511813 /* ColorPalette.xcassets */; }; 3F8B136D25D08F34004FAC0A /* HomeWidgetThisWeekData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8B136C25D08F34004FAC0A /* HomeWidgetThisWeekData.swift */; }; @@ -6369,6 +6370,7 @@ 3F8513DE260D091500A4B938 /* RoundRectangleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundRectangleView.swift; sourceTree = ""; }; 3F851414260D0A3300A4B938 /* UnifiedPrologueEditorContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnifiedPrologueEditorContentView.swift; sourceTree = ""; }; 3F851427260D1EA300A4B938 /* CircledIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircledIcon.swift; sourceTree = ""; }; + 3F86A83629D19C15005D20C0 /* SignupEpilogueTableViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupEpilogueTableViewControllerTests.swift; sourceTree = ""; }; 3F88065A26C30F2A0074DD21 /* TimeSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeSelectionViewController.swift; sourceTree = ""; }; 3F8B136C25D08F34004FAC0A /* HomeWidgetThisWeekData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeWidgetThisWeekData.swift; sourceTree = ""; }; 3F8B138E25D09AA5004FAC0A /* WordPressHomeWidgetThisWeek.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordPressHomeWidgetThisWeek.swift; sourceTree = ""; }; @@ -11219,6 +11221,14 @@ path = Editor; sourceTree = ""; }; + 3F86A83829D19C1C005D20C0 /* NUX */ = { + isa = PBXGroup; + children = ( + 3F86A83629D19C15005D20C0 /* SignupEpilogueTableViewControllerTests.swift */, + ); + path = NUX; + sourceTree = ""; + }; 3F8B459E29283D3800730FA4 /* Success card */ = { isa = PBXGroup; children = ( @@ -15270,6 +15280,7 @@ BEC8A3FD1B4BAA08001CB8C3 /* Blog */, E1B921BA1C0ED481003EA3CB /* Cells */, C3E42AAD27F4D2CF00546706 /* Menus */, + 3F86A83829D19C1C005D20C0 /* NUX */, 8B7623352384372200AB3EE7 /* Pages */, 937E3AB41E3EBDC900CDA01A /* Post */, FE3D057C26C3D5A1002A51B0 /* Sharing */, @@ -23141,6 +23152,7 @@ 805CC0BF29668A97002941DC /* MockCurrentDateProvider.swift in Sources */, 570BFD902282418A007859A8 /* PostBuilder.swift in Sources */, C738CB0F28626466001BE107 /* QRLoginScanningCoordinatorTests.swift in Sources */, + 3F86A83729D19C15005D20C0 /* SignupEpilogueTableViewControllerTests.swift in Sources */, 010459ED2915519C000C7778 /* JetpackNotificationMigrationServiceTests.swift in Sources */, 8B6214E627B1B446001DF7B6 /* BlogDashboardServiceTests.swift in Sources */, C856749A243F4292001A995E /* TenorMockDataHelper.swift in Sources */, diff --git a/WordPress/WordPressTest/NUX/SignupEpilogueTableViewControllerTests.swift b/WordPress/WordPressTest/NUX/SignupEpilogueTableViewControllerTests.swift new file mode 100644 index 000000000000..ab1e2e3e9057 --- /dev/null +++ b/WordPress/WordPressTest/NUX/SignupEpilogueTableViewControllerTests.swift @@ -0,0 +1,26 @@ +import Nimble +@testable import WordPress +import XCTest + +class SignupEpilogueTableViewControllerTests: XCTestCase { + + typealias SUT = SignupEpilogueTableViewController + + // Keeps everything before the "@" and capitalizes it + func testGenerateDisplayName() { + expect(SUT.self.generateDisplayName(from: "test@ema.il")) == "Test" + expect(SUT.self.generateDisplayName(from: "foo@email.com")) == "Foo" + } + + func testGenerateDisplayNameSplitsEmailComponents() { + expect(SUT.self.generateDisplayName(from: "test.name@ema.il")) == "Test Name" + expect(SUT.self.generateDisplayName(from: "test.name.foo@ema.il")) == "Test Name Foo" + } + + // See discussion in method definition for the rationale behind this behavior. + func testGenerateDisplayNameHandlesNonEmails() { + expect(SUT.self.generateDisplayName(from: "string")) == "String" + expect(SUT.self.generateDisplayName(from: "not.an.email")) == "Not An Email" + expect(SUT.self.generateDisplayName(from: "not an email")) == "Notanemail" + } +}