555 lines
23 KiB
Swift
555 lines
23 KiB
Swift
import AppKit
|
|
import CodexBarCore
|
|
import Testing
|
|
@testable import CodexBar
|
|
|
|
@MainActor
|
|
extension StatusMenuTests {
|
|
final class HighlightProbeView: NSView, MenuCardHighlighting {
|
|
private(set) var states: [Bool] = []
|
|
|
|
func setHighlighted(_ highlighted: Bool) {
|
|
self.states.append(highlighted)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `menu highlight updates only previous and current custom rows`() {
|
|
self.disableMenuCardsForTesting()
|
|
let settings = self.makeSettings()
|
|
let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
|
|
store._cancelPlanUtilizationHistoryLoadForTesting()
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: UsageFetcher().loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
|
|
let menu = NSMenu()
|
|
let firstView = HighlightProbeView()
|
|
let secondView = HighlightProbeView()
|
|
let thirdView = HighlightProbeView()
|
|
let first = NSMenuItem()
|
|
first.view = firstView
|
|
first.isEnabled = true
|
|
let second = NSMenuItem()
|
|
second.view = secondView
|
|
second.isEnabled = true
|
|
let third = NSMenuItem()
|
|
third.view = thirdView
|
|
third.isEnabled = true
|
|
menu.addItem(first)
|
|
menu.addItem(second)
|
|
menu.addItem(third)
|
|
|
|
controller.menu(menu, willHighlight: first)
|
|
controller.menu(menu, willHighlight: second)
|
|
controller.menu(menu, willHighlight: second)
|
|
|
|
#expect(firstView.states == [true, false])
|
|
#expect(secondView.states == [true])
|
|
#expect(thirdView.states.isEmpty)
|
|
}
|
|
|
|
@Test
|
|
func `native highlight preserves coalesced baseline resync until pointer leaves native rows`() async {
|
|
self.disableMenuCardsForTesting()
|
|
let settings = self.makeSettings()
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
|
|
store._cancelPlanUtilizationHistoryLoadForTesting()
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: UsageFetcher().loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
|
|
let menu = controller.makeMenu()
|
|
controller.menuWillOpen(menu)
|
|
defer { controller.menuDidClose(menu) }
|
|
let key = ObjectIdentifier(menu)
|
|
controller.cancelMenuWork(key)
|
|
controller.openMenus[key] = menu
|
|
let planUsage = NSMenuItem(title: "Plan Usage", action: nil, keyEquivalent: "")
|
|
planUsage.isEnabled = true
|
|
let cost = NSMenuItem(title: "Cost", action: nil, keyEquivalent: "")
|
|
cost.isEnabled = true
|
|
menu.addItem(planUsage)
|
|
menu.addItem(cost)
|
|
|
|
controller.menu(menu, willHighlight: planUsage)
|
|
#expect(controller.highlightedMenuItems[key] === planUsage)
|
|
#expect(controller.isNativeMenuItemHighlighted(in: menu))
|
|
controller.lastMenuAdjunctReadinessSignature = "stale-baseline"
|
|
controller.menuSession.invalidate(allowsStaleContent: false, requiresRebuild: true)
|
|
|
|
var rebuildCount = 0
|
|
controller._test_openMenuRebuildObserver = { _ in rebuildCount += 1 }
|
|
defer { controller._test_openMenuRebuildObserver = nil }
|
|
controller.scheduleOpenMenuRebuildIfStillVisible(
|
|
menu,
|
|
provider: .codex,
|
|
resyncReadinessBaselineAfterRebuild: true)
|
|
controller.scheduleOpenMenuRebuildIfStillVisible(menu, provider: .codex)
|
|
for _ in 0..<20 where controller.nativeHighlightDeferredMenuRebuilds[key] == nil {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 0)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key] != nil)
|
|
#expect(controller.pendingMenuBaselineResyncs.contains(key))
|
|
#expect(controller.menuNeedsRefresh(menu))
|
|
|
|
controller.menu(menu, willHighlight: cost)
|
|
for _ in 0..<20 {
|
|
await Task.yield()
|
|
}
|
|
#expect(rebuildCount == 0)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key] != nil)
|
|
|
|
controller.menu(menu, willHighlight: nil)
|
|
for _ in 0..<20 where rebuildCount == 0 {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 1)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key] == nil)
|
|
#expect(!controller.pendingMenuBaselineResyncs.contains(key))
|
|
#expect(!controller.menuNeedsRefresh(menu))
|
|
#expect(controller.lastMenuAdjunctReadinessSignature == controller.menuAdjunctReadinessSignature())
|
|
}
|
|
|
|
@Test
|
|
func `native highlight preserves explicit rebuild even when menu is already fresh`() async {
|
|
self.disableMenuCardsForTesting()
|
|
let settings = self.makeSettings()
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
|
|
store._cancelPlanUtilizationHistoryLoadForTesting()
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: UsageFetcher().loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
|
|
let menu = controller.makeMenu()
|
|
controller.populateMenu(menu, provider: .codex)
|
|
controller.markMenuFresh(menu)
|
|
let key = ObjectIdentifier(menu)
|
|
controller.openMenus[key] = menu
|
|
defer { controller.menuDidClose(menu) }
|
|
|
|
let nativeItem = NSMenuItem(title: "Plan Usage", action: nil, keyEquivalent: "")
|
|
nativeItem.isEnabled = true
|
|
menu.addItem(nativeItem)
|
|
controller.menu(menu, willHighlight: nativeItem)
|
|
#expect(!controller.menuNeedsRefresh(menu))
|
|
|
|
var rebuildCount = 0
|
|
controller._test_openMenuRebuildObserver = { _ in rebuildCount += 1 }
|
|
defer { controller._test_openMenuRebuildObserver = nil }
|
|
controller.scheduleOpenMenuRebuildIfStillVisible(menu, provider: .claude)
|
|
for _ in 0..<20 where controller.nativeHighlightDeferredMenuRebuilds[key] == nil {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 0)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key]?.provider == .claude)
|
|
#expect(!controller.menuNeedsRefresh(menu))
|
|
|
|
controller.menu(menu, willHighlight: nil)
|
|
for _ in 0..<20 where rebuildCount == 0 {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 1)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key] == nil)
|
|
#expect(!controller.menuNeedsRefresh(menu))
|
|
}
|
|
|
|
@Test
|
|
func `hosted submenu close resumes deferred explicit rebuild on fresh parent`() async {
|
|
self.disableMenuCardsForTesting()
|
|
let settings = self.makeSettings()
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
|
|
store._cancelPlanUtilizationHistoryLoadForTesting()
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: UsageFetcher().loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
controller.menuRefreshEnabledOverrideForTesting = true
|
|
|
|
let parent = controller.makeMenu()
|
|
controller.populateMenu(parent, provider: .codex)
|
|
controller.markMenuFresh(parent)
|
|
let parentKey = ObjectIdentifier(parent)
|
|
controller.openMenus[parentKey] = parent
|
|
defer { controller.menuDidClose(parent) }
|
|
|
|
let nativeItem = NSMenuItem(title: "Plan Usage", action: nil, keyEquivalent: "")
|
|
nativeItem.isEnabled = true
|
|
parent.addItem(nativeItem)
|
|
controller.menu(parent, willHighlight: nativeItem)
|
|
|
|
var rebuildCount = 0
|
|
controller._test_openMenuRebuildObserver = { menu in
|
|
if menu === parent {
|
|
rebuildCount += 1
|
|
}
|
|
}
|
|
defer { controller._test_openMenuRebuildObserver = nil }
|
|
controller.scheduleOpenMenuRebuildIfStillVisible(parent, provider: .claude)
|
|
for _ in 0..<20 where controller.nativeHighlightDeferredMenuRebuilds[parentKey] == nil {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 0)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[parentKey]?.provider == .claude)
|
|
#expect(!controller.menuNeedsRefresh(parent))
|
|
|
|
let submenu = controller.makeHostedSubviewPlaceholderMenu(
|
|
chartID: StatusItemController.costHistoryChartID,
|
|
provider: .codex)
|
|
let submenuKey = ObjectIdentifier(submenu)
|
|
controller.openMenus[submenuKey] = submenu
|
|
controller.menu(parent, willHighlight: nil)
|
|
for _ in 0..<20 {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 0)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[parentKey]?.provider == .claude)
|
|
#expect(!controller.menuNeedsRefresh(parent))
|
|
|
|
controller.menuDidClose(submenu)
|
|
for _ in 0..<40 where rebuildCount == 0 {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(controller.openMenus[submenuKey] == nil)
|
|
#expect(rebuildCount == 1)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[parentKey] == nil)
|
|
#expect(!controller.menuNeedsRefresh(parent))
|
|
}
|
|
|
|
@Test
|
|
func `hosted submenu close keeps explicit rebuild ahead of dirty parent refresh`() async {
|
|
self.disableMenuCardsForTesting()
|
|
let settings = self.makeSettings()
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
|
|
store._cancelPlanUtilizationHistoryLoadForTesting()
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: UsageFetcher().loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
controller.menuRefreshEnabledOverrideForTesting = true
|
|
|
|
let parent = controller.makeMenu()
|
|
controller.populateMenu(parent, provider: .codex)
|
|
controller.markMenuFresh(parent)
|
|
let parentKey = ObjectIdentifier(parent)
|
|
controller.openMenus[parentKey] = parent
|
|
defer { controller.menuDidClose(parent) }
|
|
|
|
let nativeItem = NSMenuItem(title: "Plan Usage", action: nil, keyEquivalent: "")
|
|
nativeItem.isEnabled = true
|
|
parent.addItem(nativeItem)
|
|
controller.menu(parent, willHighlight: nativeItem)
|
|
|
|
var rebuildCount = 0
|
|
controller._test_openMenuRebuildObserver = { menu in
|
|
if menu === parent {
|
|
rebuildCount += 1
|
|
}
|
|
}
|
|
defer { controller._test_openMenuRebuildObserver = nil }
|
|
controller.scheduleOpenMenuRebuildIfStillVisible(parent, provider: .claude)
|
|
for _ in 0..<20 where controller.nativeHighlightDeferredMenuRebuilds[parentKey] == nil {
|
|
await Task.yield()
|
|
}
|
|
|
|
let submenu = controller.makeHostedSubviewPlaceholderMenu(
|
|
chartID: StatusItemController.costHistoryChartID,
|
|
provider: .codex)
|
|
controller.openMenus[ObjectIdentifier(submenu)] = submenu
|
|
controller.menuSession.invalidate(allowsStaleContent: false, requiresRebuild: true)
|
|
#expect(controller.menuNeedsRefresh(parent))
|
|
|
|
controller.menuDidClose(submenu)
|
|
for _ in 0..<40 {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 0)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[parentKey]?.provider == .claude)
|
|
#expect(controller.menuNeedsRefresh(parent))
|
|
|
|
controller.menu(parent, willHighlight: nil)
|
|
for _ in 0..<40 where rebuildCount == 0 {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 1)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[parentKey] == nil)
|
|
#expect(!controller.menuNeedsRefresh(parent))
|
|
}
|
|
|
|
@Test
|
|
func `hosted submenu close preserves pending parent baseline resync`() async {
|
|
self.disableMenuCardsForTesting()
|
|
let settings = self.makeSettings()
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
|
|
store._cancelPlanUtilizationHistoryLoadForTesting()
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: UsageFetcher().loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
controller.menuRefreshEnabledOverrideForTesting = true
|
|
|
|
let parent = controller.makeMenu()
|
|
controller.populateMenu(parent, provider: .codex)
|
|
controller.markMenuFresh(parent)
|
|
let parentKey = ObjectIdentifier(parent)
|
|
controller.openMenus[parentKey] = parent
|
|
defer { controller.menuDidClose(parent) }
|
|
|
|
let submenu = controller.makeHostedSubviewPlaceholderMenu(
|
|
chartID: StatusItemController.costHistoryChartID,
|
|
provider: .codex)
|
|
let submenuKey = ObjectIdentifier(submenu)
|
|
controller.openMenus[submenuKey] = submenu
|
|
|
|
controller.lastMenuAdjunctReadinessSignature = "stale-baseline"
|
|
controller.menuSession.invalidate(allowsStaleContent: false, requiresRebuild: true)
|
|
var rebuildCount = 0
|
|
controller._test_openMenuRebuildObserver = { menu in
|
|
if menu === parent {
|
|
rebuildCount += 1
|
|
}
|
|
}
|
|
defer { controller._test_openMenuRebuildObserver = nil }
|
|
|
|
controller.scheduleOpenMenuRebuildIfStillVisible(
|
|
parent,
|
|
provider: .codex,
|
|
resyncReadinessBaselineAfterRebuild: true)
|
|
for _ in 0..<20 where controller.openMenuRebuildTasks[parentKey] != nil {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 0)
|
|
#expect(controller.pendingMenuBaselineResyncs.contains(parentKey))
|
|
#expect(controller.menuNeedsRefresh(parent))
|
|
|
|
controller.menuDidClose(submenu)
|
|
for _ in 0..<40 where rebuildCount == 0 {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(controller.openMenus[submenuKey] == nil)
|
|
#expect(rebuildCount == 1)
|
|
#expect(!controller.pendingMenuBaselineResyncs.contains(parentKey))
|
|
#expect(!controller.menuNeedsRefresh(parent))
|
|
#expect(controller.lastMenuAdjunctReadinessSignature == controller.menuAdjunctReadinessSignature())
|
|
}
|
|
|
|
@Test
|
|
func `menu close clears native highlight deferral and pending baseline resync`() async {
|
|
self.disableMenuCardsForTesting()
|
|
let settings = self.makeSettings()
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
|
|
store._cancelPlanUtilizationHistoryLoadForTesting()
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: UsageFetcher().loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
|
|
let menu = controller.makeMenu()
|
|
let key = ObjectIdentifier(menu)
|
|
controller.openMenus[key] = menu
|
|
let nativeItem = NSMenuItem(title: "Settings", action: nil, keyEquivalent: "")
|
|
nativeItem.isEnabled = true
|
|
menu.addItem(nativeItem)
|
|
controller.menu(menu, willHighlight: nativeItem)
|
|
controller.menuSession.invalidate(allowsStaleContent: false, requiresRebuild: true)
|
|
|
|
var rebuildCount = 0
|
|
controller._test_openMenuRebuildObserver = { _ in rebuildCount += 1 }
|
|
defer { controller._test_openMenuRebuildObserver = nil }
|
|
controller.scheduleOpenMenuRebuildIfStillVisible(
|
|
menu,
|
|
provider: .codex,
|
|
resyncReadinessBaselineAfterRebuild: true)
|
|
for _ in 0..<20 where controller.nativeHighlightDeferredMenuRebuilds[key] == nil {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key] != nil)
|
|
#expect(controller.pendingMenuBaselineResyncs.contains(key))
|
|
controller.menuDidClose(menu)
|
|
for _ in 0..<10 {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 0)
|
|
#expect(controller.openMenus[key] == nil)
|
|
#expect(controller.highlightedMenuItems[key] == nil)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key] == nil)
|
|
#expect(!controller.pendingMenuBaselineResyncs.contains(key))
|
|
#expect(controller.openMenuRebuildTasks[key] == nil)
|
|
#expect(controller.openMenuRebuildRequests.tokens[key] == nil)
|
|
}
|
|
|
|
@Test
|
|
func `hosted native highlight defers signature changing refresh until pointer leaves`() async throws {
|
|
self.disableMenuCardsForTesting()
|
|
let settings = self.makeSettings()
|
|
settings.statusChecksEnabled = true
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
|
|
store._cancelPlanUtilizationHistoryLoadForTesting()
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: UsageFetcher().loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
controller.menuRefreshEnabledOverrideForTesting = true
|
|
|
|
let submenu = NSMenu()
|
|
#expect(controller.appendStatusComponentsItem(
|
|
to: submenu,
|
|
provider: .codex,
|
|
width: StatusItemController.menuCardBaseWidth))
|
|
let key = ObjectIdentifier(submenu)
|
|
controller.openMenus[key] = submenu
|
|
defer { controller.menuDidClose(submenu) }
|
|
let originalLink = try #require(submenu.items.last)
|
|
#expect(originalLink.title == L("Open Status Page"))
|
|
#expect(originalLink.view == nil)
|
|
#expect(originalLink.isEnabled)
|
|
controller.menu(submenu, willHighlight: originalLink)
|
|
|
|
store.statusComponents[.codex] = [
|
|
ProviderStatusComponent(
|
|
id: "api",
|
|
name: "API",
|
|
indicator: .none,
|
|
status: "operational"),
|
|
]
|
|
var rebuildCount = 0
|
|
controller._test_openMenuRebuildObserver = { menu in
|
|
if menu === submenu {
|
|
rebuildCount += 1
|
|
}
|
|
}
|
|
defer { controller._test_openMenuRebuildObserver = nil }
|
|
|
|
controller.refreshOpenMenusAllowingParentRebuild()
|
|
for _ in 0..<20 where controller.nativeHighlightDeferredMenuRebuilds[key] == nil {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 0)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key] != nil)
|
|
#expect(submenu.items.count == 1)
|
|
#expect(submenu.items.first === originalLink)
|
|
|
|
controller.menu(submenu, willHighlight: nil)
|
|
for _ in 0..<20 where rebuildCount == 0 {
|
|
await Task.yield()
|
|
}
|
|
|
|
#expect(rebuildCount == 1)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key] == nil)
|
|
#expect(submenu.items.count == 3)
|
|
#expect(submenu.items.last !== originalLink)
|
|
#expect(submenu.items.last?.title == L("Open Status Page"))
|
|
}
|
|
|
|
@Test
|
|
func `custom highlight does not defer open menu rebuild`() {
|
|
self.disableMenuCardsForTesting()
|
|
let settings = self.makeSettings()
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
let store = self.makeCodexStore(settings: settings, dashboardAuthorized: false)
|
|
store._cancelPlanUtilizationHistoryLoadForTesting()
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: UsageFetcher().loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
|
|
let menu = controller.makeMenu()
|
|
controller.menuWillOpen(menu)
|
|
defer { controller.menuDidClose(menu) }
|
|
let key = ObjectIdentifier(menu)
|
|
controller.cancelMenuWork(key)
|
|
controller.openMenus[key] = menu
|
|
let customItem = NSMenuItem()
|
|
customItem.view = HighlightProbeView()
|
|
customItem.isEnabled = true
|
|
menu.addItem(customItem)
|
|
controller.menu(menu, willHighlight: customItem)
|
|
controller.menuSession.invalidate(allowsStaleContent: false, requiresRebuild: true)
|
|
|
|
var rebuildCount = 0
|
|
controller._test_openMenuRebuildObserver = { _ in rebuildCount += 1 }
|
|
defer { controller._test_openMenuRebuildObserver = nil }
|
|
controller.rebuildOpenMenuIfStillVisible(menu, provider: .codex)
|
|
|
|
#expect(rebuildCount == 1)
|
|
#expect(controller.nativeHighlightDeferredMenuRebuilds[key] == nil)
|
|
#expect(!controller.menuNeedsRefresh(menu))
|
|
}
|
|
}
|