import Testing @testable import Kanban /// **The symbol picker's pure seams** (03-board-ui.md § Styling ▸ Controls, the general-purpose /// picker `SymbolPicker.swift` builds beside the style editor's own curated grid): the curated /// default set, the full-catalog loader and its cache, and the search filter's AND semantics. /// SwiftUI rendering — the well grid, the popover's arrow-key navigation — is deliberately untested, /// exactly as `StyleEditor.swift`'s own wells are. @Suite("SymbolPicker ▸ the curated default set") struct SymbolPickerCatalogDefaultSetTests { @Test("Exactly 36 entries, all unique") func shape() { #expect(SymbolPickerCatalog.defaultSet.count == 36) #expect(Set(SymbolPickerCatalog.defaultSet).count == SymbolPickerCatalog.defaultSet.count) } @Test("Every entry is one this system can actually draw — pins the list against typos") func everyNameResolves() { let missing = SymbolPickerCatalog.defaultSet.filter { !ItemSymbol.exists($0) } #expect(missing.isEmpty, "unknown SF Symbol names: \(missing)") #expect(SymbolPickerCatalog.available.count == SymbolPickerCatalog.defaultSet.count) } } @Suite("SymbolPicker ▸ filter") struct SymbolPickerFilterTests { @Test("An empty or whitespace-only query returns the input unchanged") func emptyQueryIsANoOp() { let symbols = ["star", "flag", "heart"] #expect(SymbolPickerCatalog.filter("", in: symbols) == symbols) #expect(SymbolPickerCatalog.filter(" ", in: symbols) == symbols) #expect(SymbolPickerCatalog.filter("\t\n", in: symbols) == symbols) } @Test("A single token matches case-insensitively, as a substring") func singleTokenSubstring() { let symbols = ["star", "star.fill", "flag", "flag.checkered"] #expect(SymbolPickerCatalog.filter("star", in: symbols) == ["star", "star.fill"]) #expect(SymbolPickerCatalog.filter("STAR", in: symbols) == ["star", "star.fill"]) #expect(SymbolPickerCatalog.filter("Fla", in: symbols) == ["flag", "flag.checkered"]) } @Test("Multiple tokens are an AND — every token must appear somewhere in the name") func multiTokenIsAnAnd() { let symbols = ["wrench.and.screwdriver", "screwdriver", "wrench"] #expect(SymbolPickerCatalog.filter("wrench screw", in: symbols) == ["wrench.and.screwdriver"]) #expect(SymbolPickerCatalog.filter("screw wrench", in: symbols) == ["wrench.and.screwdriver"]) } @Test("Input order is preserved") func orderPreserved() { let symbols = ["zebra.star", "apple.star", "mango.star"] #expect(SymbolPickerCatalog.filter("star", in: symbols) == symbols) } @Test("No match returns an empty array") func noMatchIsEmpty() { #expect(SymbolPickerCatalog.filter("xyzzy-nonexistent", in: ["star", "flag"]).isEmpty) } } @Suite("SymbolPicker ▸ the full catalog") struct SymbolPickerFullCatalogTests { @Test("The default path returns a sorted, unique, non-empty list containing well-known names") func defaultPathLoads() { let catalog = SymbolPickerCatalog.fullCatalog() #expect(!catalog.isEmpty) #expect(catalog == catalog.sorted()) #expect(Set(catalog).count == catalog.count) #expect(catalog.contains("star")) #expect(catalog.contains("folder")) } @Test("Two calls against the default path return identical results — the cache is coherent") func cacheIsCoherent() { #expect(SymbolPickerCatalog.fullCatalog() == SymbolPickerCatalog.fullCatalog()) } @Test("A nonexistent bundle path falls back to the merged curated set, sorted and unique") func nonexistentPathFallsBack() { let expected = Set(SymbolPickerCatalog.defaultSet + CuratedSymbols.all).sorted() #expect(SymbolPickerCatalog.fullCatalog(bundlePath: "/nonexistent") == expected) } }