Nudge-Screen
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import Testing
|
||||
@testable import bookstax
|
||||
|
||||
// MARK: - Recent Searches
|
||||
|
||||
@Suite("SearchViewModel – Recent Searches", .serialized)
|
||||
struct SearchViewModelRecentTests {
|
||||
|
||||
private let recentKey = "recentSearches"
|
||||
|
||||
init() {
|
||||
// Start each test with a clean slate
|
||||
UserDefaults.standard.removeObject(forKey: recentKey)
|
||||
}
|
||||
|
||||
// MARK: addToRecent
|
||||
|
||||
@Test("Adding a query inserts it at position 0")
|
||||
func addInsertsAtFront() {
|
||||
let vm = SearchViewModel()
|
||||
vm.addToRecent("swift")
|
||||
vm.addToRecent("swiftui")
|
||||
let recent = vm.recentSearches
|
||||
#expect(recent.first == "swiftui")
|
||||
#expect(recent[1] == "swift")
|
||||
}
|
||||
|
||||
@Test("Adding duplicate moves it to front without creating duplicates")
|
||||
func addDeduplicates() {
|
||||
let vm = SearchViewModel()
|
||||
vm.addToRecent("bookstack")
|
||||
vm.addToRecent("wiki")
|
||||
vm.addToRecent("bookstack") // duplicate
|
||||
let recent = vm.recentSearches
|
||||
#expect(recent.first == "bookstack")
|
||||
#expect(recent.count == 2)
|
||||
#expect(!recent.dropFirst().contains("bookstack"))
|
||||
}
|
||||
|
||||
@Test("List is capped at 10 entries")
|
||||
func cappedAtTen() {
|
||||
let vm = SearchViewModel()
|
||||
for i in 1...12 {
|
||||
vm.addToRecent("query\(i)")
|
||||
}
|
||||
#expect(vm.recentSearches.count == 10)
|
||||
}
|
||||
|
||||
@Test("Oldest entries are dropped when cap is exceeded")
|
||||
func oldestDropped() {
|
||||
let vm = SearchViewModel()
|
||||
for i in 1...11 {
|
||||
vm.addToRecent("query\(i)")
|
||||
}
|
||||
let recent = vm.recentSearches
|
||||
// query1 was added first, so it falls off after 11 adds
|
||||
#expect(!recent.contains("query1"))
|
||||
#expect(recent.contains("query11"))
|
||||
}
|
||||
|
||||
// MARK: clearRecentSearches
|
||||
|
||||
@Test("clearRecentSearches empties the list")
|
||||
func clearResetsToEmpty() {
|
||||
let vm = SearchViewModel()
|
||||
vm.addToRecent("something")
|
||||
vm.clearRecentSearches()
|
||||
#expect(vm.recentSearches.isEmpty)
|
||||
}
|
||||
|
||||
// MARK: Persistence
|
||||
|
||||
@Test("Recent searches persist across ViewModel instances")
|
||||
func persistsAcrossInstances() {
|
||||
let vm1 = SearchViewModel()
|
||||
vm1.addToRecent("persistent")
|
||||
|
||||
let vm2 = SearchViewModel()
|
||||
#expect(vm2.recentSearches.contains("persistent"))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Query Minimum Length
|
||||
|
||||
@Suite("SearchViewModel – Query Logic")
|
||||
struct SearchViewModelQueryTests {
|
||||
|
||||
@Test("Short query clears results without triggering search")
|
||||
func shortQueryClearsResults() {
|
||||
let vm = SearchViewModel()
|
||||
vm.results = [SearchResultDTO(id: 1, name: "dummy", slug: "dummy",
|
||||
type: .page, url: "", preview: nil)]
|
||||
vm.query = "x" // only 1 character
|
||||
vm.onQueryChanged()
|
||||
#expect(vm.results.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Query with 2+ chars does not clear results immediately")
|
||||
func sufficientQueryKeepsResults() {
|
||||
let vm = SearchViewModel()
|
||||
vm.query = "sw" // 2 characters — triggers debounce but does not clear
|
||||
vm.onQueryChanged()
|
||||
// Results not cleared by onQueryChanged when query is long enough
|
||||
// (actual search would require API; here we just verify results aren't wiped)
|
||||
// Results were empty to start with, so we just confirm no crash
|
||||
#expect(vm.results.isEmpty) // no API call, so still empty
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user