summaryrefslogtreecommitdiff
path: root/HutchTests
diff options
context:
space:
mode:
Diffstat (limited to 'HutchTests')
-rw-r--r--HutchTests/TicketListViewModelTests.swift72
-rw-r--r--HutchTests/TicketSavedFilterStoreTests.swift88
2 files changed, 136 insertions, 24 deletions
diff --git a/HutchTests/TicketListViewModelTests.swift b/HutchTests/TicketListViewModelTests.swift
index 23be24c..0f42bf8 100644
--- a/HutchTests/TicketListViewModelTests.swift
+++ b/HutchTests/TicketListViewModelTests.swift
@@ -11,7 +11,11 @@ struct TicketListViewModelTests {
makeTicket(id: 2, title: "Already fixed", status: .resolved, submitter: "~owner", labels: [])
]
- let filtered = filterTickets(tickets, filter: .open, query: "")
+ let filtered = filterTickets(
+ tickets,
+ state: TicketListFilterState(status: .open),
+ query: ""
+ )
#expect(filtered.map(\.id) == [1])
}
@@ -23,8 +27,16 @@ struct TicketListViewModelTests {
makeTicket(id: 99, title: "Settings polish", status: .reported, submitter: "~owner", labels: [])
]
- let titleMatches = filterTickets(tickets, filter: .all, query: "settings")
- let idMatches = filterTickets(tickets, filter: .all, query: "42")
+ let titleMatches = filterTickets(
+ tickets,
+ state: TicketListFilterState(status: .all),
+ query: "settings"
+ )
+ let idMatches = filterTickets(
+ tickets,
+ state: TicketListFilterState(status: .all),
+ query: "42"
+ )
#expect(titleMatches.map(\.id) == [99])
#expect(idMatches.map(\.id) == [42])
@@ -37,14 +49,39 @@ struct TicketListViewModelTests {
makeTicket(id: 2, title: "Needs triage", status: .reported, submitter: "~triage", labels: [makeLabel(id: 2, name: "needs-info")])
]
- let submitterMatches = filterTickets(tickets, filter: .all, query: "~triage")
- let labelMatches = filterTickets(tickets, filter: .all, query: "bug")
+ let submitterMatches = filterTickets(
+ tickets,
+ state: TicketListFilterState(status: .all),
+ query: "~triage"
+ )
+ let labelMatches = filterTickets(
+ tickets,
+ state: TicketListFilterState(status: .all),
+ query: "bug"
+ )
#expect(submitterMatches.map(\.id) == [2])
#expect(labelMatches.map(\.id) == [1])
}
@Test
+ func filteredTicketsMatchesAnySelectedLabel() {
+ let tickets = [
+ makeTicket(id: 1, title: "Crash on launch", status: .reported, submitter: "~owner", labels: [makeLabel(id: 1, name: "bug")]),
+ makeTicket(id: 2, title: "Needs triage", status: .reported, submitter: "~triage", labels: [makeLabel(id: 2, name: "needs-info")]),
+ makeTicket(id: 3, title: "Unlabeled", status: .reported, submitter: "~owner", labels: [])
+ ]
+
+ let filtered = filterTickets(
+ tickets,
+ state: TicketListFilterState(status: .all, labelIDs: [2, 3]),
+ query: ""
+ )
+
+ #expect(filtered.map(\.id) == [2])
+ }
+
+ @Test
@MainActor
func resolveTicketInputHasCorrectStatusAndDefaultResolution() {
let input: [String: any Sendable] = [
@@ -65,25 +102,12 @@ struct TicketListViewModelTests {
#expect(input["resolution"] == nil)
}
- private func filterTickets(_ tickets: [TicketSummary], filter: TicketFilter, query: String) -> [TicketSummary] {
- let statusFiltered: [TicketSummary]
- switch filter {
- case .open:
- statusFiltered = tickets.filter { $0.status.isOpen }
- case .resolved:
- statusFiltered = tickets.filter { !$0.status.isOpen }
- case .all:
- statusFiltered = tickets
- }
-
- let q = query.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
- guard !q.isEmpty else { return statusFiltered }
- return statusFiltered.filter {
- String($0.id).contains(q) ||
- $0.title.lowercased().contains(q) ||
- $0.submitter.canonicalName.lowercased().contains(q) ||
- $0.labels.contains { $0.name.lowercased().contains(q) }
- }
+ private func filterTickets(
+ _ tickets: [TicketSummary],
+ state: TicketListFilterState,
+ query: String
+ ) -> [TicketSummary] {
+ TicketListViewModel.filterTickets(tickets, state: state, query: query)
}
private func makeTicket(
diff --git a/HutchTests/TicketSavedFilterStoreTests.swift b/HutchTests/TicketSavedFilterStoreTests.swift
new file mode 100644
index 0000000..2ff64e6
--- /dev/null
+++ b/HutchTests/TicketSavedFilterStoreTests.swift
@@ -0,0 +1,88 @@
+import Foundation
+import Testing
+@testable import Hutch
+
+struct TicketSavedFilterStoreTests {
+
+ @Test
+ func storesCurrentFilterStatePerTracker() {
+ let suiteName = "TicketSavedFilterStoreTests-\(#function)-\(UUID().uuidString)"
+ let defaults = UserDefaults(suiteName: suiteName)!
+ defer { defaults.removePersistentDomain(forName: suiteName) }
+
+ TicketSavedFilterStore.saveCurrentState(
+ TicketListFilterState(status: .all, labelIDs: [4, 1]),
+ for: "tracker-a",
+ defaults: defaults
+ )
+ TicketSavedFilterStore.saveCurrentState(
+ TicketListFilterState(status: .resolved, labelIDs: [9]),
+ for: "tracker-b",
+ defaults: defaults
+ )
+
+ #expect(
+ TicketSavedFilterStore.loadCurrentState(for: "tracker-a", defaults: defaults) ==
+ TicketListFilterState(status: .all, labelIDs: [1, 4])
+ )
+ #expect(
+ TicketSavedFilterStore.loadCurrentState(for: "tracker-b", defaults: defaults) ==
+ TicketListFilterState(status: .resolved, labelIDs: [9])
+ )
+ }
+
+ @Test
+ func savesNamedFiltersPerTrackerAndReplacesDuplicateNames() {
+ let suiteName = "TicketSavedFilterStoreTests-\(#function)-\(UUID().uuidString)"
+ let defaults = UserDefaults(suiteName: suiteName)!
+ defer { defaults.removePersistentDomain(forName: suiteName) }
+
+ _ = TicketSavedFilterStore.saveFilter(
+ named: "Bugs",
+ state: TicketListFilterState(status: .open, labelIDs: [1]),
+ for: "tracker-a",
+ defaults: defaults,
+ now: Date(timeIntervalSince1970: 100)
+ )
+ _ = TicketSavedFilterStore.saveFilter(
+ named: "bugs",
+ state: TicketListFilterState(status: .resolved, labelIDs: [2]),
+ for: "tracker-a",
+ defaults: defaults,
+ now: Date(timeIntervalSince1970: 200)
+ )
+ _ = TicketSavedFilterStore.saveFilter(
+ named: "Needs Info",
+ state: TicketListFilterState(status: .all, labelIDs: [3]),
+ for: "tracker-b",
+ defaults: defaults,
+ now: Date(timeIntervalSince1970: 300)
+ )
+
+ let trackerAFilters = TicketSavedFilterStore.loadSavedFilters(for: "tracker-a", defaults: defaults)
+ let trackerBFilters = TicketSavedFilterStore.loadSavedFilters(for: "tracker-b", defaults: defaults)
+
+ #expect(trackerAFilters.count == 1)
+ #expect(trackerAFilters.first?.name == "bugs")
+ #expect(trackerAFilters.first?.state == TicketListFilterState(status: .resolved, labelIDs: [2]))
+ #expect(trackerBFilters.map(\.name) == ["Needs Info"])
+ }
+
+ @Test
+ func deletesSavedFilter() {
+ let suiteName = "TicketSavedFilterStoreTests-\(#function)-\(UUID().uuidString)"
+ let defaults = UserDefaults(suiteName: suiteName)!
+ defer { defaults.removePersistentDomain(forName: suiteName) }
+
+ let savedFilter = TicketSavedFilterStore.saveFilter(
+ named: "Bugs",
+ state: TicketListFilterState(status: .open, labelIDs: [1]),
+ for: "tracker-a",
+ defaults: defaults
+ )
+
+ TicketSavedFilterStore.deleteFilter(id: savedFilter!.id, for: "tracker-a", defaults: defaults)
+
+ #expect(TicketSavedFilterStore.loadSavedFilters(for: "tracker-a", defaults: defaults).isEmpty)
+ }
+}