native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188import Foundationimport IdentifiedCollectionsimport Testing
@testable import Prowl
@MainActorstruct ToolbarNotificationGroupingTests { @Test func groupsNotificationsByRepositoryAndWorktreeInDisplayOrder() { let repoAPath = "/tmp/repo-a" let repoBPath = "/tmp/repo-b"
let repoAMain = makeWorktree(id: repoAPath, name: "main", repoRoot: repoAPath) let repoAOne = makeWorktree(id: "\(repoAPath)/one", name: "one", repoRoot: repoAPath) let repoATwo = makeWorktree(id: "\(repoAPath)/two", name: "two", repoRoot: repoAPath)
let repoBMain = makeWorktree(id: repoBPath, name: "main", repoRoot: repoBPath) let repoBOne = makeWorktree(id: "\(repoBPath)/one", name: "one", repoRoot: repoBPath)
let repoA = makeRepository(id: repoAPath, name: "Repo A", worktrees: [repoAMain, repoAOne, repoATwo]) let repoB = makeRepository(id: repoBPath, name: "Repo B", worktrees: [repoBMain, repoBOne])
var state = RepositoriesFeature.State(repositories: [repoA, repoB]) state.repositoryRoots = [repoA.rootURL, repoB.rootURL] state.repositoryOrderIDs = [repoB.id, repoA.id] state.worktreeOrderByRepository[repoA.id] = [repoATwo.id, repoAOne.id]
let manager = WorktreeTerminalManager(runtime: GhosttyRuntime()) manager.state(for: repoAOne).notifications = [ WorktreeTerminalNotification(surfaceId: UUID(), title: "A1", body: "done", isRead: true) ] manager.state(for: repoATwo).notifications = [ WorktreeTerminalNotification(surfaceId: UUID(), title: "A2", body: "done") ] manager.state(for: repoBOne).notifications = [ WorktreeTerminalNotification(surfaceId: UUID(), title: "B1", body: "done", isRead: true) ]
let groups = state.toolbarNotificationGroups(terminalManager: manager)
#expect(groups.map(\.id) == [repoB.id, repoA.id]) #expect(groups[0].worktrees.map(\.id) == [repoBOne.id]) #expect(groups[1].worktrees.map(\.id) == [repoATwo.id, repoAOne.id]) #expect(groups[1].unseenWorktreeCount == 1) }
@Test func omitsArchivedAndEmptyNotificationGroups() { let repoAPath = "/tmp/repo-a" let repoBPath = "/tmp/repo-b"
let repoAMain = makeWorktree(id: repoAPath, name: "main", repoRoot: repoAPath) let repoAArchived = makeWorktree(id: "\(repoAPath)/archived", name: "archived", repoRoot: repoAPath) let repoBMain = makeWorktree(id: repoBPath, name: "main", repoRoot: repoBPath) let repoBEmpty = makeWorktree(id: "\(repoBPath)/empty", name: "empty", repoRoot: repoBPath)
let repoA = makeRepository(id: repoAPath, name: "Repo A", worktrees: [repoAMain, repoAArchived]) let repoB = makeRepository(id: repoBPath, name: "Repo B", worktrees: [repoBMain, repoBEmpty])
var state = RepositoriesFeature.State(repositories: [repoA, repoB]) state.repositoryRoots = [repoA.rootURL, repoB.rootURL] state.archivedWorktrees = [ArchivedWorktree(id: repoAArchived.id, archivedAt: .distantPast)]
let manager = WorktreeTerminalManager(runtime: GhosttyRuntime()) manager.state(for: repoAArchived).notifications = [ WorktreeTerminalNotification(surfaceId: UUID(), title: "Archived", body: "hidden") ]
let groups = state.toolbarNotificationGroups(terminalManager: manager)
#expect(groups.isEmpty) }
@Test func unseenWorktreeCountUsesUnreadNotificationsOnly() { let repoPath = "/tmp/repo" let main = makeWorktree(id: repoPath, name: "main", repoRoot: repoPath) let readOnly = makeWorktree(id: "\(repoPath)/read-only", name: "read-only", repoRoot: repoPath) let mixed = makeWorktree(id: "\(repoPath)/mixed", name: "mixed", repoRoot: repoPath)
let repo = makeRepository(id: repoPath, name: "Repo", worktrees: [main, readOnly, mixed]) var state = RepositoriesFeature.State(repositories: [repo]) state.repositoryRoots = [repo.rootURL]
let manager = WorktreeTerminalManager(runtime: GhosttyRuntime()) manager.state(for: readOnly).notifications = [ WorktreeTerminalNotification(surfaceId: UUID(), title: "Read 1", body: "done", isRead: true) ] manager.state(for: mixed).notifications = [ WorktreeTerminalNotification(surfaceId: UUID(), title: "Read 2", body: "done", isRead: true), WorktreeTerminalNotification(surfaceId: UUID(), title: "Unread", body: "new", isRead: false), ]
let groups = state.toolbarNotificationGroups(terminalManager: manager)
#expect(groups.count == 1) #expect(groups[0].notificationCount == 3) #expect(groups[0].unseenWorktreeCount == 1) }
@Test func keepsReadOnlyNotificationsInGroups() { let repoPath = "/tmp/repo" let main = makeWorktree(id: repoPath, name: "main", repoRoot: repoPath) let feature = makeWorktree(id: "\(repoPath)/feature", name: "feature", repoRoot: repoPath)
let repo = makeRepository(id: repoPath, name: "Repo", worktrees: [main, feature]) var state = RepositoriesFeature.State(repositories: [repo]) state.repositoryRoots = [repo.rootURL]
let manager = WorktreeTerminalManager(runtime: GhosttyRuntime()) manager.state(for: feature).notifications = [ WorktreeTerminalNotification(surfaceId: UUID(), title: "Read", body: "kept", isRead: true) ]
let groups = state.toolbarNotificationGroups(terminalManager: manager)
#expect(groups.map(\.id) == [repo.id]) #expect(groups[0].worktrees.map(\.id) == [feature.id]) #expect(groups[0].unseenWorktreeCount == 0) }
@Test func customTitleOverridesGroupName() { let repoPath = "/tmp/repo" let main = makeWorktree(id: repoPath, name: "main", repoRoot: repoPath) let feature = makeWorktree(id: "\(repoPath)/feature", name: "feature", repoRoot: repoPath) let repo = makeRepository(id: repoPath, name: "Repo", worktrees: [main, feature]) var state = RepositoriesFeature.State(repositories: [repo]) state.repositoryRoots = [repo.rootURL]
let manager = WorktreeTerminalManager(runtime: GhosttyRuntime()) manager.state(for: feature).notifications = [ WorktreeTerminalNotification(surfaceId: UUID(), title: "Note", body: "done") ]
let groups = state.toolbarNotificationGroups( terminalManager: manager, customTitles: [repo.id: "Aliased Repo"] )
#expect(groups.count == 1) #expect(groups[0].name == "Aliased Repo") }
@Test func missingCustomTitleFallsBackToRepositoryName() { let repoPath = "/tmp/repo" let main = makeWorktree(id: repoPath, name: "main", repoRoot: repoPath) let feature = makeWorktree(id: "\(repoPath)/feature", name: "feature", repoRoot: repoPath) let repo = makeRepository(id: repoPath, name: "Repo", worktrees: [main, feature]) var state = RepositoriesFeature.State(repositories: [repo]) state.repositoryRoots = [repo.rootURL]
let manager = WorktreeTerminalManager(runtime: GhosttyRuntime()) manager.state(for: feature).notifications = [ WorktreeTerminalNotification(surfaceId: UUID(), title: "Note", body: "done") ]
let groups = state.toolbarNotificationGroups(terminalManager: manager, customTitles: [:])
#expect(groups.count == 1) #expect(groups[0].name == "Repo") }
private func makeWorktree( id: String, name: String, repoRoot: String ) -> Worktree { Worktree( id: id, name: name, detail: "detail", workingDirectory: URL(fileURLWithPath: id), repositoryRootURL: URL(fileURLWithPath: repoRoot) ) }
private func makeRepository( id: String, name: String, worktrees: [Worktree] ) -> Repository { Repository( id: id, rootURL: URL(fileURLWithPath: id), name: name, worktrees: IdentifiedArray(uniqueElements: worktrees) ) }}