test: add mac coverage helpers

This commit is contained in:
Peter Steinberger
2025-12-24 19:29:44 +01:00
parent f44014ff00
commit 204bd7d2c4
11 changed files with 632 additions and 71 deletions

View File

@@ -240,3 +240,20 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
}
}
}
#if DEBUG
extension CanvasSchemeHandler {
func _testResponse(for url: URL) -> (mime: String, data: Data) {
let response = self.response(for: url)
return (response.mime, response.data)
}
func _testResolveFileURL(sessionRoot: URL, requestPath: String) -> URL? {
self.resolveFileURL(sessionRoot: sessionRoot, requestPath: requestPath)
}
func _testTextEncodingName(for mimeType: String) -> String? {
self.textEncodingName(forMimeType: mimeType)
}
}
#endif

View File

@@ -748,7 +748,7 @@ private final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHan
}
}
private static func isLocalNetworkCanvasURL(_ url: URL) -> Bool {
fileprivate static func isLocalNetworkCanvasURL(_ url: URL) -> Bool {
guard let scheme = url.scheme?.lowercased(), scheme == "http" || scheme == "https" else {
return false
}
@@ -766,7 +766,7 @@ private final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHan
return false
}
private static func parseIPv4(_ host: String) -> (UInt8, UInt8, UInt8, UInt8)? {
fileprivate static func parseIPv4(_ host: String) -> (UInt8, UInt8, UInt8, UInt8)? {
let parts = host.split(separator: ".", omittingEmptySubsequences: false)
guard parts.count == 4 else { return nil }
let bytes: [UInt8] = parts.compactMap { UInt8($0) }
@@ -774,7 +774,7 @@ private final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHan
return (bytes[0], bytes[1], bytes[2], bytes[3])
}
private static func isLocalNetworkIPv4(_ ip: (UInt8, UInt8, UInt8, UInt8)) -> Bool {
fileprivate static func isLocalNetworkIPv4(_ ip: (UInt8, UInt8, UInt8, UInt8)) -> Bool {
let (a, b, _, _) = ip
if a == 10 { return true }
if a == 172, (16...31).contains(Int(b)) { return true }
@@ -1012,3 +1012,40 @@ private final class HoverChromeContainerView: NSView {
}
}
}
#if DEBUG
extension CanvasWindowController {
static func _testSanitizeSessionKey(_ key: String) -> String {
self.sanitizeSessionKey(key)
}
static func _testJSStringLiteral(_ value: String) -> String {
self.jsStringLiteral(value)
}
static func _testJSOptionalStringLiteral(_ value: String?) -> String {
self.jsOptionalStringLiteral(value)
}
static func _testStoredFrameKey(sessionKey: String) -> String {
self.storedFrameDefaultsKey(sessionKey: sessionKey)
}
static func _testStoreAndLoadFrame(sessionKey: String, frame: NSRect) -> NSRect? {
self.storeRestoredFrame(frame, sessionKey: sessionKey)
return self.loadRestoredFrame(sessionKey: sessionKey)
}
static func _testParseIPv4(_ host: String) -> (UInt8, UInt8, UInt8, UInt8)? {
CanvasA2UIActionMessageHandler.parseIPv4(host)
}
static func _testIsLocalNetworkIPv4(_ ip: (UInt8, UInt8, UInt8, UInt8)) -> Bool {
CanvasA2UIActionMessageHandler.isLocalNetworkIPv4(ip)
}
static func _testIsLocalNetworkCanvasURL(_ url: URL) -> Bool {
CanvasA2UIActionMessageHandler.isLocalNetworkCanvasURL(url)
}
}
#endif

View File

@@ -874,4 +874,56 @@ struct DebugSettings_Previews: PreviewProvider {
.frame(width: SettingsTab.windowWidth, height: SettingsTab.windowHeight)
}
}
@MainActor
extension DebugSettings {
static func exerciseForTesting() async {
var view = DebugSettings()
view.modelsCount = 3
view.modelsLoading = false
view.modelsError = "Failed to load models"
view.gatewayRootInput = "/tmp/clawdis"
view.sessionStorePath = "/tmp/sessions.json"
view.sessionStoreSaveError = "Save failed"
view.debugSendInFlight = true
view.debugSendStatus = "Sent"
view.debugSendError = "Failed"
view.portCheckInFlight = true
view.portReports = [
DebugActions.PortReport(
port: 18789,
expected: "Gateway websocket (node/tsx)",
status: .missing("Missing"),
listeners: []),
]
view.portKillStatus = "Killed"
view.pendingKill = DebugActions.PortListener(
pid: 1,
command: "node",
fullCommand: "node",
user: nil,
expected: true)
view.canvasSessionKey = "main"
view.canvasStatus = "Canvas ok"
view.canvasError = "Canvas error"
view.canvasEvalJS = "document.title"
view.canvasEvalResult = "Canvas"
view.canvasSnapshotPath = "/tmp/snapshot.png"
_ = view.body
_ = view.header
_ = view.appInfoSection
_ = view.gatewaySection
_ = view.logsSection
_ = view.portsSection
_ = view.pathsSection
_ = view.quickActionsSection
_ = view.canvasSection
_ = view.experimentsSection
_ = view.gridLabel("Test")
view.loadSessionStorePath()
await view.reloadModels()
}
}
#endif

View File

@@ -159,3 +159,23 @@ enum GatewayLaunchAgentManager {
}.value
}
}
#if DEBUG
extension GatewayLaunchAgentManager {
static func _testGatewayExecutablePath(bundlePath: String) -> String {
self.gatewayExecutablePath(bundlePath: bundlePath)
}
static func _testRelayDir(bundlePath: String) -> String {
self.relayDir(bundlePath: bundlePath)
}
static func _testPreferredGatewayBind() -> String? {
self.preferredGatewayBind()
}
static func _testPreferredGatewayToken() -> String? {
self.preferredGatewayToken()
}
}
#endif

View File

@@ -204,3 +204,25 @@ final class MenuContextCardInjector: NSObject, NSMenuDelegate {
self.lastKnownMenuWidth = max(300, targetWidth)
}
}
#if DEBUG
extension MenuContextCardInjector {
func _testSetCache(rows: [SessionRow], errorText: String?, updatedAt: Date?) {
self.cachedRows = rows
self.cacheErrorText = errorText
self.cacheUpdatedAt = updatedAt
}
func _testFindInsertIndex(in menu: NSMenu) -> Int? {
self.findInsertIndex(in: menu)
}
func _testInitialCardWidth(for menu: NSMenu) -> CGFloat {
self.initialCardWidth(for: menu)
}
func _testCachedView() -> AnyView {
self.cachedView()
}
}
#endif

View File

@@ -153,58 +153,7 @@ actor PortGuardian {
for port in ports {
let listeners = await self.listeners(on: port)
let expectedDesc: String
let okPredicate: (Listener) -> Bool
let expectedCommands = ["node", "clawdis", "tsx", "pnpm", "bun"]
switch mode {
case .remote:
expectedDesc = "SSH tunnel to remote gateway"
okPredicate = { $0.command.lowercased().contains("ssh") }
case .local:
expectedDesc = "Gateway websocket (node/tsx)"
okPredicate = { listener in
let c = listener.command.lowercased()
return expectedCommands.contains { c.contains($0) }
}
case .unconfigured:
expectedDesc = "Gateway not configured"
okPredicate = { _ in false }
}
if listeners.isEmpty {
let text = "Nothing is listening on \(port) (\(expectedDesc))."
reports.append(.init(port: port, expected: expectedDesc, status: .missing(text), listeners: []))
continue
}
let reportListeners = listeners.map { listener in
ReportListener(
pid: listener.pid,
command: listener.command,
fullCommand: listener.fullCommand,
user: listener.user,
expected: okPredicate(listener))
}
let offenders = reportListeners.filter { !$0.expected }
if offenders.isEmpty {
let list = listeners.map { "\($0.command) (\($0.pid))" }.joined(separator: ", ")
let okText = "Port \(port) is served by \(list)."
reports.append(.init(
port: port,
expected: expectedDesc,
status: .ok(okText),
listeners: reportListeners))
} else {
let list = offenders.map { "\($0.command) (\($0.pid))" }.joined(separator: ", ")
let reason = "Port \(port) is held by \(list), expected \(expectedDesc)."
reports.append(.init(
port: port,
expected: expectedDesc,
status: .interference(reason, offenders: offenders),
listeners: reportListeners))
}
reports.append(Self.buildReport(port: port, listeners: listeners, mode: mode))
}
return reports
@@ -218,6 +167,29 @@ actor PortGuardian {
timeout: 5)
guard res.ok, let data = res.payload, !data.isEmpty else { return [] }
let text = String(data: data, encoding: .utf8) ?? ""
return Self.parseListeners(from: text)
}
private static func readFullCommand(pid: Int32) -> String? {
let proc = Process()
proc.executableURL = URL(fileURLWithPath: "/bin/ps")
proc.arguments = ["-p", "\(pid)", "-o", "command="]
let pipe = Pipe()
proc.standardOutput = pipe
proc.standardError = Pipe()
do {
try proc.run()
proc.waitUntilExit()
} catch {
return nil
}
let data = pipe.fileHandleForReading.readToEndSafely()
guard !data.isEmpty else { return nil }
return String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines)
}
private static func parseListeners(from text: String) -> [Listener] {
var listeners: [Listener] = []
var currentPid: Int32?
var currentCmd: String?
@@ -252,23 +224,62 @@ actor PortGuardian {
return listeners
}
private static func readFullCommand(pid: Int32) -> String? {
let proc = Process()
proc.executableURL = URL(fileURLWithPath: "/bin/ps")
proc.arguments = ["-p", "\(pid)", "-o", "command="]
let pipe = Pipe()
proc.standardOutput = pipe
proc.standardError = Pipe()
do {
try proc.run()
proc.waitUntilExit()
} catch {
return nil
private static func buildReport(
port: Int,
listeners: [Listener],
mode: AppState.ConnectionMode) -> PortReport
{
let expectedDesc: String
let okPredicate: (Listener) -> Bool
let expectedCommands = ["node", "clawdis", "tsx", "pnpm", "bun"]
switch mode {
case .remote:
expectedDesc = "SSH tunnel to remote gateway"
okPredicate = { $0.command.lowercased().contains("ssh") }
case .local:
expectedDesc = "Gateway websocket (node/tsx)"
okPredicate = { listener in
let c = listener.command.lowercased()
return expectedCommands.contains { c.contains($0) }
}
case .unconfigured:
expectedDesc = "Gateway not configured"
okPredicate = { _ in false }
}
let data = pipe.fileHandleForReading.readToEndSafely()
guard !data.isEmpty else { return nil }
return String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines)
if listeners.isEmpty {
let text = "Nothing is listening on \(port) (\(expectedDesc))."
return .init(port: port, expected: expectedDesc, status: .missing(text), listeners: [])
}
let reportListeners = listeners.map { listener in
ReportListener(
pid: listener.pid,
command: listener.command,
fullCommand: listener.fullCommand,
user: listener.user,
expected: okPredicate(listener))
}
let offenders = reportListeners.filter { !$0.expected }
if offenders.isEmpty {
let list = listeners.map { "\($0.command) (\($0.pid))" }.joined(separator: ", ")
let okText = "Port \(port) is served by \(list)."
return .init(
port: port,
expected: expectedDesc,
status: .ok(okText),
listeners: reportListeners)
}
let list = offenders.map { "\($0.command) (\($0.pid))" }.joined(separator: ", ")
let reason = "Port \(port) is held by \(list), expected \(expectedDesc)."
return .init(
port: port,
expected: expectedDesc,
status: .interference(reason, offenders: offenders),
listeners: reportListeners)
}
private static func executablePath(for pid: Int32) -> String? {
@@ -319,3 +330,20 @@ actor PortGuardian {
try? data.write(to: Self.recordPath, options: [.atomic])
}
}
#if DEBUG
extension PortGuardian {
static func _testParseListeners(_ text: String) -> [(pid: Int32, command: String, fullCommand: String, user: String?)] {
Self.parseListeners(from: text).map { ($0.pid, $0.command, $0.fullCommand, $0.user) }
}
static func _testBuildReport(
port: Int,
mode: AppState.ConnectionMode,
listeners: [(pid: Int32, command: String, fullCommand: String, user: String?)]) -> PortReport
{
let mapped = listeners.map { Listener(pid: $0.pid, command: $0.command, fullCommand: $0.fullCommand, user: $0.user) }
return Self.buildReport(port: port, listeners: mapped, mode: mode)
}
}
#endif

View File

@@ -132,3 +132,27 @@ final class PresenceReporter {
return en0 ?? fallback
}
}
#if DEBUG
extension PresenceReporter {
static func _testComposePresenceSummary(mode: String, reason: String) -> String {
self.composePresenceSummary(mode: mode, reason: reason)
}
static func _testAppVersionString() -> String {
self.appVersionString()
}
static func _testPlatformString() -> String {
self.platformString()
}
static func _testLastInputSeconds() -> Int? {
self.lastInputSeconds()
}
static func _testPrimaryIPv4Address() -> String? {
self.primaryIPv4Address()
}
}
#endif

View File

@@ -17,3 +17,13 @@ extension View {
.onPreferenceChange(ViewWidthPreferenceKey.self, perform: onChange)
}
}
#if DEBUG
enum ViewMetricsTesting {
static func reduceWidth(current: CGFloat, next: CGFloat) -> CGFloat {
var value = current
ViewWidthPreferenceKey.reduce(value: &value, nextValue: { next })
return value
}
}
#endif

View File

@@ -510,4 +510,33 @@ struct VoiceWakeSettings_Previews: PreviewProvider {
.frame(width: SettingsTab.windowWidth, height: SettingsTab.windowHeight)
}
}
@MainActor
extension VoiceWakeSettings {
static func exerciseForTesting() {
let state = AppState(preview: true)
state.swabbleEnabled = true
state.voicePushToTalkEnabled = true
state.swabbleTriggerWords = ["Claude", "Hey"]
var view = VoiceWakeSettings(state: state)
view.availableMics = [AudioInputDevice(uid: "mic-1", name: "Built-in")]
view.availableLocales = [Locale(identifier: "en_US")]
view.meterLevel = 0.42
view.meterError = "No input"
view.testState = .detected("ok")
view.isTesting = true
_ = view.body
_ = view.localePicker
_ = view.micPicker
_ = view.levelMeter
_ = view.triggerTable
_ = view.chimeSection
view.addWord()
_ = view.binding(for: 0).wrappedValue
view.removeWord(at: 0)
}
}
#endif

View File

@@ -0,0 +1,224 @@
import AppKit
import Foundation
import Testing
@testable import Clawdis
@Suite(.serialized)
struct LowCoverageHelperTests {
@Test func anyCodableHelperAccessors() throws {
let payload: [String: AnyCodable] = [
"title": AnyCodable("Hello"),
"flag": AnyCodable(true),
"count": AnyCodable(3),
"ratio": AnyCodable(1.25),
"list": AnyCodable([AnyCodable("a"), AnyCodable(2)]),
]
let any = AnyCodable(payload)
let dict = try #require(any.dictionaryValue)
#expect(dict["title"]?.stringValue == "Hello")
#expect(dict["flag"]?.boolValue == true)
#expect(dict["count"]?.intValue == 3)
#expect(dict["ratio"]?.doubleValue == 1.25)
#expect(dict["list"]?.arrayValue?.count == 2)
let foundation = any.foundationValue as? [String: Any]
#expect(foundation?["title"] as? String == "Hello")
}
@Test func attributedStringStripsForegroundColor() {
let text = NSMutableAttributedString(string: "Test")
text.addAttribute(.foregroundColor, value: NSColor.red, range: NSRange(location: 0, length: 4))
let stripped = text.strippingForegroundColor()
let color = stripped.attribute(.foregroundColor, at: 0, effectiveRange: nil)
#expect(color == nil)
}
@Test func viewMetricsReduceWidth() {
let value = ViewMetricsTesting.reduceWidth(current: 120, next: 180)
#expect(value == 180)
}
@Test func shellExecutorHandlesEmptyCommand() async {
let result = await ShellExecutor.runDetailed(command: [], cwd: nil, env: nil, timeout: nil)
#expect(result.success == false)
#expect(result.errorMessage != nil)
}
@Test func shellExecutorRunsCommand() async {
let result = await ShellExecutor.runDetailed(command: ["/bin/echo", "ok"], cwd: nil, env: nil, timeout: 2)
#expect(result.success == true)
#expect(result.stdout.contains("ok") || result.stderr.contains("ok"))
}
@Test func shellExecutorTimesOut() async {
let result = await ShellExecutor.runDetailed(command: ["/bin/sleep", "1"], cwd: nil, env: nil, timeout: 0.05)
#expect(result.timedOut == true)
}
@Test func pairedNodesStorePersists() async throws {
let dir = FileManager.default.temporaryDirectory
.appendingPathComponent("paired-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
let url = dir.appendingPathComponent("nodes.json")
let store = PairedNodesStore(fileURL: url)
await store.load()
#expect(await store.all().isEmpty)
let node = PairedNode(
nodeId: "node-1",
displayName: "Node One",
platform: "macOS",
version: "1.0",
deviceFamily: "Mac",
modelIdentifier: "MacBookPro",
token: "token",
createdAtMs: 1,
lastSeenAtMs: nil)
try await store.upsert(node)
#expect(await store.find(nodeId: "node-1")?.displayName == "Node One")
try await store.touchSeen(nodeId: "node-1")
let updated = await store.find(nodeId: "node-1")
#expect(updated?.lastSeenAtMs != nil)
}
@Test @MainActor func presenceReporterHelpers() {
let summary = PresenceReporter._testComposePresenceSummary(mode: "local", reason: "test")
#expect(summary.contains("mode local"))
#expect(!PresenceReporter._testAppVersionString().isEmpty)
#expect(!PresenceReporter._testPlatformString().isEmpty)
_ = PresenceReporter._testLastInputSeconds()
_ = PresenceReporter._testPrimaryIPv4Address()
}
@Test func gatewayLaunchAgentHelpers() {
let keyBind = "CLAWDIS_GATEWAY_BIND"
let keyToken = "CLAWDIS_GATEWAY_TOKEN"
let previousBind = ProcessInfo.processInfo.environment[keyBind]
let previousToken = ProcessInfo.processInfo.environment[keyToken]
defer {
if let previousBind {
setenv(keyBind, previousBind, 1)
} else {
unsetenv(keyBind)
}
if let previousToken {
setenv(keyToken, previousToken, 1)
} else {
unsetenv(keyToken)
}
}
setenv(keyBind, "Lan", 1)
setenv(keyToken, " secret ", 1)
#expect(GatewayLaunchAgentManager._testPreferredGatewayBind() == "lan")
#expect(GatewayLaunchAgentManager._testPreferredGatewayToken() == "secret")
#expect(GatewayLaunchAgentManager._testGatewayExecutablePath(bundlePath: "/App") == "/App/Contents/Resources/Relay/clawdis")
#expect(GatewayLaunchAgentManager._testRelayDir(bundlePath: "/App") == "/App/Contents/Resources/Relay")
}
@Test func portGuardianParsesListenersAndBuildsReports() {
let output = """
p123
cnode
uuser
p456
cssh
uroot
"""
let listeners = PortGuardian._testParseListeners(output)
#expect(listeners.count == 2)
#expect(listeners[0].command == "node")
#expect(listeners[1].command == "ssh")
let okReport = PortGuardian._testBuildReport(
port: 18789,
mode: .local,
listeners: [(pid: 1, command: "node", fullCommand: "node", user: "me")])
#expect(okReport.offenders.isEmpty)
let badReport = PortGuardian._testBuildReport(
port: 18789,
mode: .local,
listeners: [(pid: 2, command: "python", fullCommand: "python", user: "me")])
#expect(!badReport.offenders.isEmpty)
let emptyReport = PortGuardian._testBuildReport(port: 18789, mode: .local, listeners: [])
#expect(emptyReport.summary.contains("Nothing is listening"))
}
@Test @MainActor func canvasSchemeHandlerResolvesFilesAndErrors() throws {
let root = FileManager.default.temporaryDirectory
.appendingPathComponent("canvas-\(UUID().uuidString)", isDirectory: true)
defer { try? FileManager.default.removeItem(at: root) }
try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
let session = root.appendingPathComponent("main", isDirectory: true)
try FileManager.default.createDirectory(at: session, withIntermediateDirectories: true)
let index = session.appendingPathComponent("index.html")
try "<h1>Hello</h1>".write(to: index, atomically: true, encoding: .utf8)
let handler = CanvasSchemeHandler(root: root)
let url = try #require(CanvasScheme.makeURL(session: "main", path: "index.html"))
let response = handler._testResponse(for: url)
#expect(response.mime == "text/html")
#expect(String(data: response.data, encoding: .utf8)?.contains("Hello") == true)
let invalid = URL(string: "https://example.com")!
let invalidResponse = handler._testResponse(for: invalid)
#expect(invalidResponse.mime == "text/html")
let missing = try #require(CanvasScheme.makeURL(session: "missing", path: "/"))
let missingResponse = handler._testResponse(for: missing)
#expect(missingResponse.mime == "text/html")
#expect(handler._testTextEncodingName(for: "text/html") == "utf-8")
#expect(handler._testTextEncodingName(for: "application/octet-stream") == nil)
}
@Test @MainActor func menuContextCardInjectorInsertsAndFindsIndex() {
let injector = MenuContextCardInjector()
let menu = NSMenu()
menu.minimumWidth = 280
menu.addItem(NSMenuItem(title: "Active", action: nil, keyEquivalent: ""))
menu.addItem(.separator())
menu.addItem(NSMenuItem(title: "Send Heartbeats", action: nil, keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "Quit", action: nil, keyEquivalent: "q"))
let idx = injector._testFindInsertIndex(in: menu)
#expect(idx == 1)
#expect(injector._testInitialCardWidth(for: menu) >= 300)
injector._testSetCache(rows: [SessionRow.previewRows[0]], errorText: nil, updatedAt: Date())
injector.menuWillOpen(menu)
injector.menuDidClose(menu)
let fallbackMenu = NSMenu()
fallbackMenu.addItem(NSMenuItem(title: "First", action: nil, keyEquivalent: ""))
#expect(injector._testFindInsertIndex(in: fallbackMenu) == 1)
}
@Test @MainActor func canvasWindowHelperFunctions() {
#expect(CanvasWindowController._testSanitizeSessionKey(" main ") == "main")
#expect(CanvasWindowController._testSanitizeSessionKey("bad/..") == "bad___")
#expect(CanvasWindowController._testJSOptionalStringLiteral(nil) == "null")
let rect = NSRect(x: 10, y: 12, width: 400, height: 420)
let key = CanvasWindowController._testStoredFrameKey(sessionKey: "test")
let loaded = CanvasWindowController._testStoreAndLoadFrame(sessionKey: "test", frame: rect)
UserDefaults.standard.removeObject(forKey: key)
#expect(loaded?.size.width == rect.size.width)
let parsed = CanvasWindowController._testParseIPv4("192.168.1.2")
#expect(parsed != nil)
if let parsed {
#expect(CanvasWindowController._testIsLocalNetworkIPv4(parsed))
}
let url = URL(string: "http://192.168.1.2")!
#expect(CanvasWindowController._testIsLocalNetworkCanvasURL(url))
#expect(CanvasWindowController._testParseIPv4("not-an-ip") == nil)
}
}

View File

@@ -0,0 +1,98 @@
import AppKit
import SwiftUI
import Testing
@testable import Clawdis
@Suite(.serialized)
@MainActor
struct LowCoverageViewSmokeTests {
@Test func contextMenuCardBuildsBody() {
let loading = ContextMenuCardView(rows: [], statusText: "Loading…", isLoading: true)
_ = loading.body
let empty = ContextMenuCardView(rows: [], statusText: nil, isLoading: false)
_ = empty.body
let withRows = ContextMenuCardView(rows: SessionRow.previewRows, statusText: nil, isLoading: false)
_ = withRows.body
}
@Test func settingsToggleRowBuildsBody() {
var flag = false
let binding = Binding(get: { flag }, set: { flag = $0 })
let view = SettingsToggleRow(title: "Enable", subtitle: "Detail", binding: binding)
_ = view.body
}
@Test func voiceWakeTestCardBuildsBodyAcrossStates() {
var state = VoiceWakeTestState.idle
var isTesting = false
let stateBinding = Binding(get: { state }, set: { state = $0 })
let testingBinding = Binding(get: { isTesting }, set: { isTesting = $0 })
_ = VoiceWakeTestCard(testState: stateBinding, isTesting: testingBinding, onToggle: {}).body
state = .hearing("hello")
_ = VoiceWakeTestCard(testState: stateBinding, isTesting: testingBinding, onToggle: {}).body
state = .detected("command")
isTesting = true
_ = VoiceWakeTestCard(testState: stateBinding, isTesting: testingBinding, onToggle: {}).body
state = .failed("No mic")
_ = VoiceWakeTestCard(testState: stateBinding, isTesting: testingBinding, onToggle: {}).body
}
@Test func agentEventsWindowBuildsBodyWithEvent() {
AgentEventStore.shared.clear()
let sample = ControlAgentEvent(
runId: "run-1",
seq: 1,
stream: "tool",
ts: Date().timeIntervalSince1970 * 1000,
data: ["phase": AnyCodable("start"), "name": AnyCodable("test")],
summary: nil)
AgentEventStore.shared.append(sample)
_ = AgentEventsWindow().body
AgentEventStore.shared.clear()
}
@Test func notifyOverlayPresentsAndDismisses() async {
let controller = NotifyOverlayController()
controller.present(title: "Hello", body: "World", autoDismissAfter: 0)
controller.present(title: "Updated", body: "Again", autoDismissAfter: 0)
controller.dismiss()
try? await Task.sleep(nanoseconds: 250_000_000)
}
@Test func visualEffectViewHostsInNSHostingView() {
let hosting = NSHostingView(rootView: VisualEffectView(material: .sidebar))
_ = hosting.fittingSize
hosting.rootView = VisualEffectView(material: .popover, emphasized: true)
_ = hosting.fittingSize
}
@Test func menuHostedItemHostsContent() {
let view = MenuHostedItem(width: 240, rootView: AnyView(Text("Menu")))
let hosting = NSHostingView(rootView: view)
_ = hosting.fittingSize
hosting.rootView = MenuHostedItem(width: 320, rootView: AnyView(Text("Updated")))
_ = hosting.fittingSize
}
@Test func dockIconManagerUpdatesVisibility() {
_ = NSApplication.shared
UserDefaults.standard.set(false, forKey: showDockIconKey)
DockIconManager.shared.updateDockVisibility()
DockIconManager.shared.temporarilyShowDock()
}
@Test func voiceWakeSettingsExercisesHelpers() {
VoiceWakeSettings.exerciseForTesting()
}
@Test func debugSettingsExercisesHelpers() async {
await DebugSettings.exerciseForTesting()
}
}