howl.command
local cmd, readline
before_each ->
readline = {
read: spy.new (rl, prompt, input, opts = {}) ->
rl.prompt = prompt
rl.text = opts.text or ''
}
app.window = :readline
cmd = name: 'foo', description: 'desc', handler: spy.new -> true
after_each ->
command.unregister name for name in *command.names!
app.window = nil
.names() returns a list of all command names
command.register cmd
assert.includes command.names!, 'foo'
.<name> allows direct indexing of commands
command.register cmd
assert.equal command.foo.handler, cmd.handler
.get(name) returns the command with the specified name
command.register cmd
assert.equal command.get('foo').handler, cmd.handler
allows a registered command to be invoked directly
cmd.handler = (num) -> num * 2
command.register cmd
assert.equal command.foo(2), 4
.unregister(command) removes the command and any aliases
command.register cmd
command.alias 'foo', 'bar'
command.unregister 'foo'
assert.is_nil command.foo
assert.is_nil command.bar
assert.same command.names!, {}
.register(command)
raises an error if any of the mandatory fields are missing
assert.raises 'name', -> command.register {}
assert.raises 'description', -> command.register name: 'foo'
assert.raises 'handler', -> command.register name: 'foo', description: 'do'
.alias(target, name)
raises an error if target does not exist
assert.raises 'exist', -> command.alias 'nothing', 'something'
allows for multiple names for the same command
command.register cmd
command.alias 'foo', 'bar'
assert.equal 'foo', command.bar.alias_for
assert.includes command.names!, 'bar'
(when command name is a non-lua identifier)
before_each -> cmd.name = 'foo-cmd:bar'
register() adds accessible aliases for the direct indexing
command.register cmd
assert.equal command['foo-cmd:bar'].handler, cmd.handler
assert.equal command.foo_cmd_bar.handler, cmd.handler
the accessible alias is not part of names()
command.register cmd
assert.same command.names!, { 'foo-cmd:bar' }
unregister() removes the accessible name as well
command.register cmd
command.unregister 'foo-cmd:bar'
assert.is_nil command.foo_cmd_bar
.run(cmd_string)
local input
before_each ->
inputs.register {
name: 'test_input',
description: 'command spec input',
factory: ->
input = {
complete: -> 'completions'
should_complete: -> 'perhaps'
close_on_cancel: -> true
value_for: -> 123
on_completed: spy.new -> nil
go_back: spy.new -> nil
on_cancelled: spy.new -> nil
}
input
}
after_each -> inputs.unregister 'test_input'
input = value_for: -> 'yay'
cmd.input = spy.new -> input
command.register cmd
command.run cmd.name
assert.spy(cmd.input).was.called 1
(when <cmd_string> is empty or missing)
invokes howl.app.window.readline with a ":" prompt
command.run!
assert.spy(readline.read).was_called!
assert.equals ':', readline.prompt
(when <cmd_string> is given)
(.. and it matches a simple command without parameters)
that command is invoked direcly
command.register cmd
command.run cmd.name
assert.spy(cmd.handler).was_called
(.. when it specifies a command with parameters)
invokes howl.app.window.readline
cmd.input = 'test_input'
command.register cmd
command.run cmd.name
assert.spy(cmd.handler).was_not_called(1)
assert.spy(readline.read).was_called(1)
assert.equals "#{cmd.name} ", readline.text
instantiates the input with any additional text
cmd.input = spy.new -> {}
command.register cmd
command.run "#{cmd.name} hello"
assert.spy(readline.read).was_called(1)
assert.spy(cmd.input).was_called_with 'hello'
assert.equals "#{cmd.name} hello", readline.text
(.. when it specifies a unknown command)
invokes readline.read with the text set to the given string
command.run 'what-the-heck now'
assert.spy(readline.read).was_called(1)
assert.equals 'what-the-heck now', readline.text
(interacting with readline)
local cmd_input, readline, handler, co, return_value
run = (...) ->
f = coroutine.wrap (...) -> command.run ...
return_value = f ...
fake_return = (...) ->
coroutine.resume co, ...
before_each ->
readline = read: (prompt, i) =>
co = coroutine.running!
cmd_input = i
@prompt = prompt
@text = ''
coroutine.yield!
app.window = :readline
handler = spy.new -> nil
command.register {
name: 'p_cmd',
description: 'desc',
:handler
input: 'test_input'
}
after_each -> command.unregister name for name in *command.names!
(.. when entering a command)
should_complete(..) returns false
run!
assert.is_false cmd_input\should_complete '', readline
complete(..) returns a list of command names, key bindings, and descriptions
keymap.ctrl_shift_p = 'p_cmd'
run!
completions = cmd_input\complete '', readline
assert.same completions, { { 'p_cmd', 'ctrl_shift_p', 'desc' } }
(.. when entering command arguments)
run!
cmd_input\update 'p_cmd first', readline
assert.not_nil input
assert.equal cmd_input\complete(readline.text, readline), input.complete!
assert.equal cmd_input\should_complete(readline.text, readline), input.should_complete!
assert.equal cmd_input\close_on_cancel(readline.text, readline), input.close_on_cancel!
cmd_input\on_completed(readline.text, readline)
assert.spy(input.on_completed).was_called 1
cmd_input\go_back(readline)
assert.spy(input.go_back).was_called 1
cmd_input\on_cancelled(readline)
assert.spy(input.on_cancelled).was_called 1
updates the readline prompt to include the command name when it is complete
run!
cmd_input\update 'p_cmd first', readline
assert.match readline.prompt, 'p_cmd $'
runs the command when the user submits
run!
readline.text = 'p_cmd first'
cmd_input\update readline.text, readline
fake_return 'first'
assert.spy(handler).was_called_with input\value_for!
(.. keymap handling)
run!
cmd_input\update 'p_cmd first', readline
assert.not_nil input
input.keymap = a: 1
assert.equal 1, cmd_input.keymap.a
run!
cmd_input\update 'p_cmd first', readline
assert.not_nil input
input.keymap = a: spy.new -> true
cmd_input.keymap.a cmd_input, readline, nil
assert.spy(input.keymap.a).was_called_with input, readline, nil
(.. when the user submits an unknown command)
the on_submit callback returns false to keep readline open
run!
assert.is_false cmd_input\on_submit 'unknowncommand', readline
(.. when the user submits a known command but with too few arguments)
the on_submit callback returns false to keep readline open
run!
assert.is_false cmd_input\on_submit 'p_cmd', readline
the command is added to the readline prompt
run!
readline.text = 'p_cmd'
cmd_input\update readline.text, readline
cmd_input\on_submit 'p_cmd', readline
assert.match readline.prompt, 'p_cmd $'