chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
import AppKit
|
||||
import CodexBarCore
|
||||
import Testing
|
||||
@testable import CodexBar
|
||||
|
||||
@MainActor
|
||||
@Suite(.serialized)
|
||||
struct StatusMenuSwitcherTrackingTests {
|
||||
@Test
|
||||
func `switcher rebuild scheduler runs during menu tracking exactly once`() {
|
||||
var runCount = 0
|
||||
ProviderSwitcherTrackingRunLoopScheduler.schedule {
|
||||
runCount += 1
|
||||
}
|
||||
|
||||
CFRunLoopRunInMode(
|
||||
CFRunLoopMode(RunLoop.Mode.eventTracking.rawValue as CFString),
|
||||
0.1,
|
||||
true)
|
||||
#expect(runCount == 1)
|
||||
|
||||
CFRunLoopRunInMode(.defaultMode, 0.1, true)
|
||||
#expect(runCount == 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `pointer switch defers structural menu rebuild until mouse up`() async throws {
|
||||
let previousMenuCardRendering = StatusItemController.menuCardRenderingEnabled
|
||||
let previousMenuRefresh = StatusItemController.menuRefreshEnabled
|
||||
StatusItemController.menuCardRenderingEnabled = false
|
||||
StatusItemController.setMenuRefreshEnabledForTesting(true)
|
||||
defer {
|
||||
StatusItemController.menuCardRenderingEnabled = previousMenuCardRendering
|
||||
StatusItemController.setMenuRefreshEnabledForTesting(previousMenuRefresh)
|
||||
}
|
||||
|
||||
let settings = self.makeSettings()
|
||||
settings.statusChecksEnabled = false
|
||||
settings.refreshFrequency = .manual
|
||||
settings.mergeIcons = true
|
||||
settings.selectedMenuProvider = .codex
|
||||
settings.mergedMenuLastSelectedWasOverview = false
|
||||
|
||||
let registry = ProviderRegistry.shared
|
||||
for provider in UsageProvider.allCases {
|
||||
guard let metadata = registry.metadata[provider] else { continue }
|
||||
settings.setProviderEnabled(
|
||||
provider: provider,
|
||||
metadata: metadata,
|
||||
enabled: provider == .codex || provider == .claude)
|
||||
}
|
||||
|
||||
let fetcher = UsageFetcher()
|
||||
let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)
|
||||
let controller = StatusItemController(
|
||||
store: store,
|
||||
settings: settings,
|
||||
account: fetcher.loadAccountInfo(),
|
||||
updater: DisabledUpdaterController(),
|
||||
preferencesSelection: PreferencesSelection(),
|
||||
statusBar: .system,
|
||||
menuRefreshEnabled: false)
|
||||
defer { controller.releaseStatusItemsForTesting() }
|
||||
|
||||
let menu = controller.makeMenu()
|
||||
controller.menuWillOpen(menu)
|
||||
controller.menuRefreshEnabledOverrideForTesting = true
|
||||
controller.openMenus[ObjectIdentifier(menu)] = menu
|
||||
let switcher = try #require(menu.items.first?.view as? ProviderSwitcherView)
|
||||
let mouseDown = try #require(switcher._test_mouseDownEvent(buttonTag: 2))
|
||||
let mouseUp = try #require(switcher._test_mouseUpEvent(buttonTag: 2))
|
||||
|
||||
var rebuildCount = 0
|
||||
controller._test_openMenuRebuildObserver = { _ in
|
||||
rebuildCount += 1
|
||||
}
|
||||
defer { controller._test_openMenuRebuildObserver = nil }
|
||||
|
||||
#expect(controller.handleProviderSwitcherTrackingEvent(mouseDown, menu: menu))
|
||||
#expect(settings.selectedMenuProvider == .codex)
|
||||
#expect(controller.providerSwitcherPointerInteractionMenuID == ObjectIdentifier(menu))
|
||||
#expect(controller.pendingProviderSwitcherPointerRebuild == nil)
|
||||
|
||||
for _ in 0..<20 {
|
||||
await Task.yield()
|
||||
}
|
||||
#expect(rebuildCount == 0)
|
||||
|
||||
#expect(controller.handleProviderSwitcherTrackingEvent(mouseUp, menu: menu))
|
||||
#expect(settings.selectedMenuProvider == .claude)
|
||||
for _ in 0..<100 where rebuildCount == 0 {
|
||||
await Task.yield()
|
||||
try? await Task.sleep(for: .milliseconds(5))
|
||||
}
|
||||
#expect(rebuildCount == 1)
|
||||
#expect(controller.providerSwitcherPointerInteractionMenuID == nil)
|
||||
#expect(controller.pendingProviderSwitcherPointerRebuild == nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `pointer switch cancels when mouse up leaves pressed segment`() throws {
|
||||
let previousMenuCardRendering = StatusItemController.menuCardRenderingEnabled
|
||||
let previousMenuRefresh = StatusItemController.menuRefreshEnabled
|
||||
StatusItemController.menuCardRenderingEnabled = false
|
||||
StatusItemController.setMenuRefreshEnabledForTesting(true)
|
||||
defer {
|
||||
StatusItemController.menuCardRenderingEnabled = previousMenuCardRendering
|
||||
StatusItemController.setMenuRefreshEnabledForTesting(previousMenuRefresh)
|
||||
}
|
||||
|
||||
let settings = self.makeSettings()
|
||||
settings.statusChecksEnabled = false
|
||||
settings.refreshFrequency = .manual
|
||||
settings.mergeIcons = true
|
||||
settings.selectedMenuProvider = .codex
|
||||
settings.mergedMenuLastSelectedWasOverview = false
|
||||
|
||||
let registry = ProviderRegistry.shared
|
||||
for provider in UsageProvider.allCases {
|
||||
guard let metadata = registry.metadata[provider] else { continue }
|
||||
settings.setProviderEnabled(
|
||||
provider: provider,
|
||||
metadata: metadata,
|
||||
enabled: provider == .codex || provider == .claude)
|
||||
}
|
||||
|
||||
let fetcher = UsageFetcher()
|
||||
let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)
|
||||
let controller = StatusItemController(
|
||||
store: store,
|
||||
settings: settings,
|
||||
account: fetcher.loadAccountInfo(),
|
||||
updater: DisabledUpdaterController(),
|
||||
preferencesSelection: PreferencesSelection(),
|
||||
statusBar: .system,
|
||||
menuRefreshEnabled: false)
|
||||
defer { controller.releaseStatusItemsForTesting() }
|
||||
|
||||
let menu = controller.makeMenu()
|
||||
controller.menuWillOpen(menu)
|
||||
controller.menuRefreshEnabledOverrideForTesting = true
|
||||
controller.openMenus[ObjectIdentifier(menu)] = menu
|
||||
let switcher = try #require(menu.items.first?.view as? ProviderSwitcherView)
|
||||
let mouseDown = try #require(switcher._test_mouseDownEvent(buttonTag: 2))
|
||||
let mouseUpElsewhere = try #require(switcher._test_mouseUpEvent(buttonTag: 1))
|
||||
|
||||
var rebuildCount = 0
|
||||
controller._test_openMenuRebuildObserver = { _ in
|
||||
rebuildCount += 1
|
||||
}
|
||||
defer { controller._test_openMenuRebuildObserver = nil }
|
||||
|
||||
#expect(controller.handleProviderSwitcherTrackingEvent(mouseDown, menu: menu))
|
||||
#expect(controller.handleProviderSwitcherTrackingEvent(mouseUpElsewhere, menu: menu))
|
||||
#expect(settings.selectedMenuProvider == .codex)
|
||||
#expect(rebuildCount == 0)
|
||||
#expect(controller.providerSwitcherPointerInteractionMenuID == nil)
|
||||
#expect(controller.pendingProviderSwitcherPointerRebuild == nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `unrelated mouse up remains available to normal menu items`() throws {
|
||||
let settings = self.makeSettings()
|
||||
settings.statusChecksEnabled = false
|
||||
settings.refreshFrequency = .manual
|
||||
settings.mergeIcons = true
|
||||
|
||||
let fetcher = UsageFetcher()
|
||||
let controller = StatusItemController(
|
||||
store: UsageStore(
|
||||
fetcher: fetcher,
|
||||
browserDetection: BrowserDetection(cacheTTL: 0),
|
||||
settings: settings),
|
||||
settings: settings,
|
||||
account: fetcher.loadAccountInfo(),
|
||||
updater: DisabledUpdaterController(),
|
||||
preferencesSelection: PreferencesSelection(),
|
||||
statusBar: .system)
|
||||
defer { controller.releaseStatusItemsForTesting() }
|
||||
|
||||
let menu = NSMenu()
|
||||
let switcher = ProviderSwitcherView(
|
||||
providers: [.codex, .claude],
|
||||
selected: .provider(.codex),
|
||||
includesOverview: true,
|
||||
width: 320,
|
||||
showsIcons: false,
|
||||
iconProvider: { _ in NSImage() },
|
||||
weeklyRemainingProvider: { _ in nil },
|
||||
onSelect: { _ in })
|
||||
let item = NSMenuItem()
|
||||
item.view = switcher
|
||||
menu.addItem(item)
|
||||
let unrelatedMouseUp = try #require(switcher._test_mouseUpEvent(buttonTag: 1))
|
||||
|
||||
#expect(!controller.handleProviderSwitcherTrackingEvent(unrelatedMouseUp, menu: menu))
|
||||
}
|
||||
|
||||
private func makeSettings() -> SettingsStore {
|
||||
let suite = "StatusMenuSwitcherTrackingTests-\(UUID().uuidString)"
|
||||
let defaults = UserDefaults(suiteName: suite)!
|
||||
defaults.removePersistentDomain(forName: suite)
|
||||
return SettingsStore(
|
||||
userDefaults: defaults,
|
||||
configStore: testConfigStore(suiteName: suite),
|
||||
zaiTokenStore: NoopZaiTokenStore(),
|
||||
syntheticTokenStore: NoopSyntheticTokenStore())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user