Skip to content

Commit

Permalink
Autofill iOS de-duplication of prompted logins (#501)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/1203822806345703/1205369440069075/f
iOS PR: duckduckgo/iOS#1995
macOS PR: duckduckgo/macos-browser#1612
What kind of version bump will this require?: Minor

Description:
Brings iOS into parity with macOS de-duplication of logins when prompting to autofill
  • Loading branch information
amddg44 authored Sep 18, 2023
1 parent ffb111c commit ef045a2
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public struct AccountMatches {
}

public protocol AutofillAccountMatcher {
func findMatchesSortedByLastUpdated(accounts: [SecureVaultModels.WebsiteAccount], for url: String) -> AccountMatches
func findDeduplicatedSortedMatches(accounts: [SecureVaultModels.WebsiteAccount], for url: String) -> AccountMatches
func findMatches(accounts: [SecureVaultModels.WebsiteAccount], for url: String) -> [SecureVaultModels.WebsiteAccount]
}

Expand All @@ -46,10 +46,9 @@ public struct AutofillWebsiteAccountMatcher: AutofillAccountMatcher {
self.tld = tld
}

@available(*, deprecated, message: "Use findMatches(accounts:for:) -- Which returns an already sorted list with removed duplicates")
public func findMatchesSortedByLastUpdated(accounts: [SecureVaultModels.WebsiteAccount], for url: String) -> AccountMatches {
let matches = buildMatches(accounts: accounts, for: url)
return sort(matches: matches)
public func findDeduplicatedSortedMatches(accounts: [SecureVaultModels.WebsiteAccount], for url: String) -> AccountMatches {
let deduplicatedAndSortedMatches = findMatches(accounts: accounts, for: url)
return buildMatches(accounts: deduplicatedAndSortedMatches, for: url)
}

/// Builds a list of accounts that are perfect matches for the given url
Expand Down Expand Up @@ -79,12 +78,6 @@ public struct AutofillWebsiteAccountMatcher: AutofillAccountMatcher {
return AccountMatches(perfectMatches: perfectMatches, partialMatches: partialMatches)
}

private func sort(matches: AccountMatches) -> AccountMatches {
let sortedPerfectMatches = matches.perfectMatches.sorted { $0.lastUpdated > $1.lastUpdated }
let partialMatches = matches.partialMatches.mapValues { $0.sorted { $0.lastUpdated > $1.lastUpdated } }
return AccountMatches(perfectMatches: sortedPerfectMatches, partialMatches: partialMatches)
}

public func findMatches(accounts: [SecureVaultModels.WebsiteAccount], for url: String) -> [SecureVaultModels.WebsiteAccount] {
return accounts.sortedForDomain(url, tld: tld, removeDuplicates: true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class AutofillWebsiteAccountMatcherTests: XCTestCase {

func testWhenOnlyOnePerfectMatchThenCorrectlyPutIntoPerfectMatches() {
let accounts = [websiteAccountFor(domain: "example.com")]
let matches = autofillWebsiteAccountMatcher.findMatchesSortedByLastUpdated(accounts: accounts, for: "example.com")
let matches = autofillWebsiteAccountMatcher.findDeduplicatedSortedMatches(accounts: accounts, for: "example.com")
XCTAssertEqual(matches.perfectMatches.count, 1)
XCTAssertEqual(matches.partialMatches.count, 0)
}
Expand All @@ -49,29 +49,29 @@ final class AutofillWebsiteAccountMatcherTests: XCTestCase {
let accounts = [websiteAccountFor(domain: "example.com"),
websiteAccountFor(domain: "example.com"),
websiteAccountFor(domain: "example.com")]
let matches = autofillWebsiteAccountMatcher.findMatchesSortedByLastUpdated(accounts: accounts, for: "example.com")
let matches = autofillWebsiteAccountMatcher.findDeduplicatedSortedMatches(accounts: accounts, for: "example.com")
XCTAssertEqual(matches.perfectMatches.count, 3)
XCTAssertEqual(matches.partialMatches.count, 0)
}

func testWhenNotAMatchThenNotIncludedInGroups() {
let accounts = [websiteAccountFor(domain: "example.com")]
let matches = autofillWebsiteAccountMatcher.findMatchesSortedByLastUpdated(accounts: accounts, for: "example.org")
let matches = autofillWebsiteAccountMatcher.findDeduplicatedSortedMatches(accounts: accounts, for: "example.org")
XCTAssertEqual(matches.perfectMatches.count, 0)
XCTAssertEqual(matches.partialMatches.count, 0)
}

func testWhenSinglePartialMatchThenGetsItsOwnGroup() {
let accounts = [websiteAccountFor(domain: "foo.example.com")]
let matches = autofillWebsiteAccountMatcher.findMatchesSortedByLastUpdated(accounts: accounts, for: "example.com")
let matches = autofillWebsiteAccountMatcher.findDeduplicatedSortedMatches(accounts: accounts, for: "example.com")
XCTAssertEqual(matches.perfectMatches.count, 0)
XCTAssertEqual(matches.partialMatches.count, 1)
}

func testWhenMultiplePartialMatchesWithSameSubdomainThenAllShareAGroup() {
let accounts = [websiteAccountFor(domain: "foo.example.com"),
websiteAccountFor(domain: "foo.example.com")]
let matches = autofillWebsiteAccountMatcher.findMatchesSortedByLastUpdated(accounts: accounts, for: "example.com")
let matches = autofillWebsiteAccountMatcher.findDeduplicatedSortedMatches(accounts: accounts, for: "example.com")
XCTAssertEqual(matches.perfectMatches.count, 0)
XCTAssertEqual(matches.partialMatches.count, 1)
XCTAssertEqual(matches.partialMatches["foo.example.com"]?.count, 2)
Expand All @@ -81,7 +81,7 @@ final class AutofillWebsiteAccountMatcherTests: XCTestCase {
let accounts = [websiteAccountFor(domain: "foo.example.com"),
websiteAccountFor(domain: "bar.example.com"),
websiteAccountFor(domain: "bar.example.com")]
let matches = autofillWebsiteAccountMatcher.findMatchesSortedByLastUpdated(accounts: accounts, for: "example.com")
let matches = autofillWebsiteAccountMatcher.findDeduplicatedSortedMatches(accounts: accounts, for: "example.com")
XCTAssertEqual(matches.perfectMatches.count, 0)
XCTAssertEqual(matches.partialMatches.count, 2)
XCTAssertEqual(matches.partialMatches["foo.example.com"]?.count, 1)
Expand All @@ -90,24 +90,24 @@ final class AutofillWebsiteAccountMatcherTests: XCTestCase {

func testWhenSortingPerfectMatchesThenLastEditedSortedFirst() {
let now = Date()
let accounts = [websiteAccountFor(domain: "example.com", lastUpdated: now.addingTimeInterval(-100)),
websiteAccountFor(domain: "example.com", lastUpdated: now.addingTimeInterval(-10)),
let accounts = [websiteAccountFor(domain: "example.com", lastUpdated: now.addingTimeInterval(-24*60*60*2)),
websiteAccountFor(domain: "example.com", lastUpdated: now.addingTimeInterval(-24*60*60)),
websiteAccountFor(domain: "example.com", lastUpdated: now.addingTimeInterval(-1))]
let matches = autofillWebsiteAccountMatcher.findMatchesSortedByLastUpdated(accounts: accounts, for: "example.com")
let matches = autofillWebsiteAccountMatcher.findDeduplicatedSortedMatches(accounts: accounts, for: "example.com")
XCTAssertEqual(matches.perfectMatches[0].lastUpdated, now.addingTimeInterval(-1))
XCTAssertEqual(matches.perfectMatches[1].lastUpdated, now.addingTimeInterval(-10))
XCTAssertEqual(matches.perfectMatches[2].lastUpdated, now.addingTimeInterval(-100))
XCTAssertEqual(matches.perfectMatches[1].lastUpdated, now.addingTimeInterval(-24*60*60))
XCTAssertEqual(matches.perfectMatches[2].lastUpdated, now.addingTimeInterval(-24*60*60*2))
}

func testWhenSortingPartialMatchesThenLastEditedSortedFirst() {
let now = Date()
let accounts = [websiteAccountFor(domain: "foo.example.com", lastUpdated: now.addingTimeInterval(-100)),
websiteAccountFor(domain: "foo.example.com", lastUpdated: now.addingTimeInterval(-10)),
let accounts = [websiteAccountFor(domain: "foo.example.com", lastUpdated: now.addingTimeInterval(-24*60*60*2)),
websiteAccountFor(domain: "foo.example.com", lastUpdated: now.addingTimeInterval(-24*60*60)),
websiteAccountFor(domain: "foo.example.com", lastUpdated: now.addingTimeInterval(-1))]
let matches = autofillWebsiteAccountMatcher.findMatchesSortedByLastUpdated(accounts: accounts, for: "example.com")
let matches = autofillWebsiteAccountMatcher.findDeduplicatedSortedMatches(accounts: accounts, for: "example.com")
XCTAssertEqual(matches.partialMatches["foo.example.com"]?[0].lastUpdated, now.addingTimeInterval(-1))
XCTAssertEqual(matches.partialMatches["foo.example.com"]?[1].lastUpdated, now.addingTimeInterval(-10))
XCTAssertEqual(matches.partialMatches["foo.example.com"]?[2].lastUpdated, now.addingTimeInterval(-100))
XCTAssertEqual(matches.partialMatches["foo.example.com"]?[1].lastUpdated, now.addingTimeInterval(-24*60*60))
XCTAssertEqual(matches.partialMatches["foo.example.com"]?[2].lastUpdated, now.addingTimeInterval(-24*60*60*2))
}

func websiteAccountFor(domain: String = "", lastUpdated: Date = Date()) -> SecureVaultModels.WebsiteAccount {
Expand Down

0 comments on commit ef045a2

Please sign in to comment.