A mock of the Hammerspoon hs global for testing Spoons
under busted.
hs_mock.new() returns a fresh hs and the
ctl test controls. The mock is strict: reading an
hs.<name> it doesn’t provide raises
hs_mock: hs.<name> is not mocked; tests can add or
replace anything.
Mocked: logger; timer
(doAfter, doEvery, new,
secondsSinceEpoch, absoluteTime);
task; menubar; notify;
caffeinate.watcher; application
(find, get, watcher,
launchOrFocus, launchOrFocusByBundleID,
frontmostApplication); hotkey.bind;
audiodevice; image
(imageFromName, iconForFile);
execute; open; ipc (present);
alert; settings;
screen.mainScreen; mouse.absolutePosition;
eventtap (keyStroke, leftClick,
new, event); websocket;
wifi; host.interfaceStyle;
distributednotifications; spoons.resourcePath
(returns the file unchanged); json.read,
write; fs.attributes, mkdir; and
Hammerspoon’s own fnutils and inspect,
vendored. Event constants match Hammerspoon 1.1.1.
json.read and json.write copy, like real
serialization.
function hs_mock.new()
-> hs: table
2. ctl: HsMockCtlBuilds a fresh mock; call it from before_each.
return hs — The mock hs
global; assign it to _G.hs.
return ctl — The test controls.
Test controls returned by hs_mock.new().
function HsMockCtl.activate(notification: table, activationType: integer)Activates a notification with a
hs.notify.activationTypes value and calls its callback.
function HsMockCtl.addAudioDevice(attributes: { name: string, uid: string, input: boolean, transport: string, muted: boolean, volume: number, inUse: boolean })
-> device: tableAdds an audio device: an output, or an input with
input = true; uid defaults to
name.
function HsMockCtl.advance(seconds: number)Moves the clock, firing due timers in due-time order (ties in
scheduling order); doEvery and started
hs.timer.new timers re-arm.
{ id: string, text: string, style: table, screen: table, seconds: number, closed: boolean }[]One entry per hs.alert.show, which returns the string
id.
function HsMockCtl.appEvent(name: string, event: integer, app?: table)Delivers a hs.application.watcher event to the started
watchers.
table[]Watchers from hs.application.watcher.new.
table<string, table>Apps hs.application.find and get return: by
key, else by bundleID() among these and
frontmost.
{ outputs: table[], inputs: table[], defaultOutput: table, defaultInput: table }Audio devices and defaults; setDefault*Device() updates
the defaults.
function HsMockCtl.audioEvent(event: string)Fires the running hs.audiodevice.watcher callback.
function HsMockCtl.caffeinate(event: integer)Delivers a hs.caffeinate.watcher event to the started
watchers.
table[]Watchers from hs.caffeinate.watcher.new.
{ x: number, y: number }[]Points passed to hs.eventtap.leftClick.
function HsMockCtl.deviceEvent(device: table, event: string, scope: string, element: integer)Calls a device’s watcher callback, if it is watching.
table<string, boolean>Directories made by hs.fs.mkdir.
function HsMockCtl.distributedNotification(name: string, object?: any, userInfo?: table)Delivers a notification to running watchers registered for its name, or for all.
string[]Commands passed to hs.execute.
table<string, any>The files behind hs.json and hs.fs; a
non-table value reads as malformed JSON.
function HsMockCtl.flush()Fires the one-shot timers pending now, whatever their delay, moving the clock to each; leaves repeating timers and ones scheduled meanwhile.
table?What hs.application.frontmostApplication returns.
{ mods: string[], key: string, message: string, pressed: function, released: function, repeated: function, enabled: boolean, deleted: boolean }[]Bindings made with hs.hotkey.bind.
table<string, any>What hs.image.imageFromName / iconForFile
return; unknown names give nil, as in Hammerspoon.
string?What hs.host.interfaceStyle returns: nil
(light) or "Dark", as macOS reports.
{ mods: string[], key: string, delay: number, app: table }[]Calls to hs.eventtap.keyStroke.
string[]Names and bundle ids passed to launchOrFocus /
launchOrFocusByBundleID.
function HsMockCtl.logged(level: "debug"|"error"|"info"|"verbose"|"warning")
-> string[]The messages logged at one level.
level:
| "error"
| "warning"
| "info"
| "debug"
| "verbose"{ logger: string, level: string, message: string }[]Every log line at every level.
HsMockMenubar[]Menu bar items created by hs.menubar.new.
{ x: number, y: number }What hs.mouse.absolutePosition reads and sets.
function HsMockCtl.newEvent(attributes: { type: integer, systemKey: { key: string, down: boolean }, flags: table })
-> event: tableBuilds an event, as hs.eventtap.event constructors
would.
table[]Watchers from hs.distributednotifications.new.
table[]Notifications from hs.notify.new, keeping
_attributes, _sent and
_withdrawn.
numberVirtual clock in seconds; drives
hs.timer.secondsSinceEpoch and absoluteTime
(integer nanoseconds).
(fun(command: string, withUserEnv?: boolean):string, boolean, string, integer)?Answers hs.execute; the default returns
"", true, "exit", 0.
fun(task: HsMockTask):integer?, string?, string??Called on task:start(); returning an exit code completes
the task at once.
string[]Paths passed to hs.open.
table[]Events posted with event:post().
{ x: number, y: number, w: number, h: number }What hs.screen.mainScreen():frame() returns (a
copy).
table<string, any>Backing store for hs.settings.
function HsMockCtl.tapEvent(event: table)
-> answer: anyDelivers an event to the first enabled tap watching its type.
return answer — What that tap’s
callback returned, or nil if none matched.
table[]Event taps from hs.eventtap.new.
HsMockTask[]Tasks created by hs.task.new.
function HsMockCtl.timers()
-> table[]The running timers, in creation order.
function HsMockCtl.websocketEvent(ws: table, event: string, message?: any)Calls a socket’s callback.
table[]Sockets from hs.websocket.new, keeping
_url, _sent and _closed.
function HsMockCtl.wifiEvent(message?: string, interface?: string)Notifies the running wifi watchers.
param message — Defaults to
"SSIDChange".
param interface — Defaults to
"en0".
string?What hs.wifi.currentNetwork returns.
table[]Watchers from hs.wifi.watcher.new.
A task from hs.task.new, keeping _path,
_args, _running and
_terminated.
booleanboolean(method) HsMockTask:closeInput()
-> HsMockTask(method) HsMockTask:complete(exitCode: integer, stdout?: string, stderr?: string)Finishes the task and calls its callback, as the process exiting would.
param stdout — Defaults to
"".
param stderr — Defaults to
"".
(method) HsMockTask:isRunning()
-> boolean(method) HsMockTask:pid()
-> unknown(method) HsMockTask:start()
-> HsMockTask(method) HsMockTask:terminate()
-> HsMockTaskA menu bar item from hs.menubar.new; state set through
its methods is kept in _title, _icon,
_iconTemplate, _menu, _tooltip,
_autosaveName, _inMenuBar and
_deleted.
any(method) HsMockMenubar:_click()Calls the click callback, as clicking the menu bar item would.
anybooleananyanybooleananyanyany(method) HsMockMenubar:autosaveName(name: any)
-> HsMockMenubar(method) HsMockMenubar:delete()(method) HsMockMenubar:frame()
-> table(method) HsMockMenubar:isInMenuBar()
-> boolean(method) HsMockMenubar:removeFromMenuBar()
-> HsMockMenubar(method) HsMockMenubar:returnToMenuBar()
-> HsMockMenubar(method) HsMockMenubar:setClickCallback(fn: any)
-> HsMockMenubar(method) HsMockMenubar:setIcon(icon: any, template: any)
-> HsMockMenubar(method) HsMockMenubar:setMenu(menu: any)
-> HsMockMenubar(method) HsMockMenubar:setTitle(title: any)
-> HsMockMenubar(method) HsMockMenubar:setTooltip(tooltip: any)
-> HsMockMenubar(method) HsMockMenubar:title()
-> unknown