test: expand overlay coverage
This commit is contained in:
@@ -631,3 +631,81 @@ enum CritterIconRenderer {
|
|||||||
canvas.context.restoreGState()
|
canvas.context.restoreGState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
@MainActor
|
||||||
|
extension CritterStatusLabel {
|
||||||
|
static func exerciseForTesting() async {
|
||||||
|
var label = CritterStatusLabel(
|
||||||
|
isPaused: false,
|
||||||
|
isSleeping: false,
|
||||||
|
isWorking: true,
|
||||||
|
earBoostActive: false,
|
||||||
|
blinkTick: 1,
|
||||||
|
sendCelebrationTick: 1,
|
||||||
|
gatewayStatus: .running(details: nil),
|
||||||
|
animationsEnabled: true,
|
||||||
|
iconState: .workingMain(.tool(.bash)))
|
||||||
|
|
||||||
|
_ = label.body
|
||||||
|
_ = label.iconImage
|
||||||
|
_ = label.tickTaskID
|
||||||
|
label.tick(Date())
|
||||||
|
label.resetMotion()
|
||||||
|
label.blink()
|
||||||
|
label.wiggle()
|
||||||
|
label.wiggleLegs()
|
||||||
|
label.wiggleEars()
|
||||||
|
label.scurry()
|
||||||
|
label.scheduleRandomTimers(from: Date())
|
||||||
|
_ = label.gatewayNeedsAttention
|
||||||
|
_ = label.gatewayBadgeColor
|
||||||
|
|
||||||
|
label.isPaused = true
|
||||||
|
_ = label.iconImage
|
||||||
|
|
||||||
|
label.isPaused = false
|
||||||
|
label.isSleeping = true
|
||||||
|
_ = label.iconImage
|
||||||
|
|
||||||
|
label.isSleeping = false
|
||||||
|
label.iconState = .idle
|
||||||
|
_ = label.iconImage
|
||||||
|
|
||||||
|
let failed = CritterStatusLabel(
|
||||||
|
isPaused: false,
|
||||||
|
isSleeping: false,
|
||||||
|
isWorking: false,
|
||||||
|
earBoostActive: false,
|
||||||
|
blinkTick: 0,
|
||||||
|
sendCelebrationTick: 0,
|
||||||
|
gatewayStatus: .failed("boom"),
|
||||||
|
animationsEnabled: false,
|
||||||
|
iconState: .idle)
|
||||||
|
_ = failed.gatewayNeedsAttention
|
||||||
|
_ = failed.gatewayBadgeColor
|
||||||
|
|
||||||
|
let stopped = CritterStatusLabel(
|
||||||
|
isPaused: false,
|
||||||
|
isSleeping: false,
|
||||||
|
isWorking: false,
|
||||||
|
earBoostActive: false,
|
||||||
|
blinkTick: 0,
|
||||||
|
sendCelebrationTick: 0,
|
||||||
|
gatewayStatus: .stopped,
|
||||||
|
animationsEnabled: false,
|
||||||
|
iconState: .idle)
|
||||||
|
_ = stopped.gatewayNeedsAttention
|
||||||
|
_ = stopped.gatewayBadgeColor
|
||||||
|
|
||||||
|
_ = CritterIconRenderer.makeIcon(
|
||||||
|
blink: 0.6,
|
||||||
|
legWiggle: 0.8,
|
||||||
|
earWiggle: 0.4,
|
||||||
|
earScale: 1.4,
|
||||||
|
earHoles: true,
|
||||||
|
eyesClosedLines: true,
|
||||||
|
badge: .init(symbolName: "gearshape.fill", prominence: .secondary))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ final class HoverHUDController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func installDismissMonitor() {
|
private func installDismissMonitor() {
|
||||||
|
if ProcessInfo.processInfo.isRunningTests { return }
|
||||||
guard self.dismissMonitor == nil, let window else { return }
|
guard self.dismissMonitor == nil, let window else { return }
|
||||||
self.dismissMonitor = NSEvent.addGlobalMonitorForEvents(matching: [
|
self.dismissMonitor = NSEvent.addGlobalMonitorForEvents(matching: [
|
||||||
.leftMouseDown,
|
.leftMouseDown,
|
||||||
|
|||||||
@@ -271,6 +271,9 @@ final class PermissionMonitor {
|
|||||||
private func startMonitoring() {
|
private func startMonitoring() {
|
||||||
Task { await self.checkStatus(force: true) }
|
Task { await self.checkStatus(force: true) }
|
||||||
|
|
||||||
|
if ProcessInfo.processInfo.isRunningTests {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.monitorTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
|
self.monitorTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ final class VoicePushToTalkHotkey: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setEnabled(_ enabled: Bool) {
|
func setEnabled(_ enabled: Bool) {
|
||||||
|
if ProcessInfo.processInfo.isRunningTests { return }
|
||||||
self.withMainThread { [weak self] in
|
self.withMainThread { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
if enabled {
|
if enabled {
|
||||||
|
|||||||
@@ -855,3 +855,51 @@ private final class TranscriptNSTextView: NSTextView {
|
|||||||
super.keyDown(with: event)
|
super.keyDown(with: event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
@MainActor
|
||||||
|
extension VoiceWakeOverlayController {
|
||||||
|
static func exerciseForTesting() async {
|
||||||
|
let controller = VoiceWakeOverlayController(enableUI: false)
|
||||||
|
let token = controller.startSession(
|
||||||
|
source: .wakeWord,
|
||||||
|
transcript: "Hello",
|
||||||
|
attributed: nil,
|
||||||
|
forwardEnabled: true,
|
||||||
|
isFinal: false)
|
||||||
|
|
||||||
|
controller.updatePartial(token: token, transcript: "Hello world")
|
||||||
|
controller.presentFinal(token: token, transcript: "Final", autoSendAfter: nil)
|
||||||
|
controller.userBeganEditing()
|
||||||
|
controller.endEditing()
|
||||||
|
controller.updateText("Edited text")
|
||||||
|
|
||||||
|
_ = controller.makeAttributed(from: "Attributed")
|
||||||
|
_ = controller.targetFrame()
|
||||||
|
_ = controller.measuredHeight()
|
||||||
|
_ = controller.dismissTargetFrame(
|
||||||
|
for: NSRect(x: 0, y: 0, width: 120, height: 60),
|
||||||
|
reason: .empty,
|
||||||
|
outcome: .empty)
|
||||||
|
_ = controller.dismissTargetFrame(
|
||||||
|
for: NSRect(x: 0, y: 0, width: 120, height: 60),
|
||||||
|
reason: .explicit,
|
||||||
|
outcome: .sent)
|
||||||
|
_ = controller.dismissTargetFrame(
|
||||||
|
for: NSRect(x: 0, y: 0, width: 120, height: 60),
|
||||||
|
reason: .explicit,
|
||||||
|
outcome: .empty)
|
||||||
|
|
||||||
|
controller.beginSendUI(token: token, sendChime: .none)
|
||||||
|
try? await Task.sleep(nanoseconds: 350_000_000)
|
||||||
|
|
||||||
|
controller.scheduleAutoSend(token: token, after: 10)
|
||||||
|
controller.autoSendTask?.cancel()
|
||||||
|
controller.autoSendTask = nil
|
||||||
|
controller.autoSendToken = nil
|
||||||
|
|
||||||
|
controller.dismiss(token: token, reason: .explicit, outcome: .sent)
|
||||||
|
controller.bringToFrontIfVisible()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ final class WebChatSwiftUIWindowController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func installDismissMonitor() {
|
private func installDismissMonitor() {
|
||||||
|
if ProcessInfo.processInfo.isRunningTests { return }
|
||||||
guard self.dismissMonitor == nil, self.window != nil else { return }
|
guard self.dismissMonitor == nil, self.window != nil else { return }
|
||||||
self.dismissMonitor = NSEvent.addGlobalMonitorForEvents(
|
self.dismissMonitor = NSEvent.addGlobalMonitorForEvents(
|
||||||
matching: [.leftMouseDown, .rightMouseDown, .otherMouseDown])
|
matching: [.leftMouseDown, .rightMouseDown, .otherMouseDown])
|
||||||
|
|||||||
@@ -30,5 +30,8 @@ struct CritterIconRendererTests {
|
|||||||
|
|
||||||
#expect(image.tiffRepresentation != nil)
|
#expect(image.tiffRepresentation != nil)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@Test func critterStatusLabelExercisesHelpers() async {
|
||||||
|
await CritterStatusLabel.exerciseForTesting()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import AppKit
|
||||||
|
import Testing
|
||||||
|
@testable import Clawdis
|
||||||
|
|
||||||
|
@Suite(.serialized)
|
||||||
|
@MainActor
|
||||||
|
struct HoverHUDControllerTests {
|
||||||
|
@Test func hoverHUDControllerPresentsAndDismisses() async {
|
||||||
|
let controller = HoverHUDController()
|
||||||
|
controller.setSuppressed(false)
|
||||||
|
|
||||||
|
controller.statusItemHoverChanged(
|
||||||
|
inside: true,
|
||||||
|
anchorProvider: { NSRect(x: 10, y: 10, width: 24, height: 24) })
|
||||||
|
try? await Task.sleep(nanoseconds: 260_000_000)
|
||||||
|
|
||||||
|
controller.panelHoverChanged(inside: true)
|
||||||
|
controller.panelHoverChanged(inside: false)
|
||||||
|
controller.statusItemHoverChanged(
|
||||||
|
inside: false,
|
||||||
|
anchorProvider: { NSRect(x: 10, y: 10, width: 24, height: 24) })
|
||||||
|
|
||||||
|
controller.dismiss(reason: "test")
|
||||||
|
controller.setSuppressed(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import Testing
|
|||||||
@Suite(.serialized)
|
@Suite(.serialized)
|
||||||
@MainActor
|
@MainActor
|
||||||
struct VoiceWakeOverlayControllerTests {
|
struct VoiceWakeOverlayControllerTests {
|
||||||
@Test func overlayControllerLifecycleWithoutUI() {
|
@Test func overlayControllerLifecycleWithoutUI() async {
|
||||||
let controller = VoiceWakeOverlayController(enableUI: false)
|
let controller = VoiceWakeOverlayController(enableUI: false)
|
||||||
let token = controller.startSession(
|
let token = controller.startSession(
|
||||||
source: .wakeWord,
|
source: .wakeWord,
|
||||||
@@ -22,6 +22,7 @@ struct VoiceWakeOverlayControllerTests {
|
|||||||
|
|
||||||
controller.updateLevel(token: token, -0.5)
|
controller.updateLevel(token: token, -0.5)
|
||||||
#expect(controller.model.level == 0)
|
#expect(controller.model.level == 0)
|
||||||
|
try? await Task.sleep(nanoseconds: 120_000_000)
|
||||||
controller.updateLevel(token: token, 2.0)
|
controller.updateLevel(token: token, 2.0)
|
||||||
#expect(controller.model.level == 1)
|
#expect(controller.model.level == 1)
|
||||||
|
|
||||||
@@ -60,4 +61,8 @@ struct VoiceWakeOverlayControllerTests {
|
|||||||
controller.updateLevel(token: token, 0.9)
|
controller.updateLevel(token: token, 0.9)
|
||||||
#expect(controller.model.level == 0.9)
|
#expect(controller.model.level == 0.9)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test func overlayControllerExercisesHelpers() async {
|
||||||
|
await VoiceWakeOverlayController.exerciseForTesting()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user