pulsar/spec/app/keymap-spec.coffee
Nathan Sobo f2f44b9ab6 Can match key patterns with the '-' character
Add a parser to parse keystroke patterns instead of splitting on '-' with a regex
2012-05-09 10:56:31 -06:00

227 lines
9.9 KiB
CoffeeScript

Keymap = require 'keymap'
$ = require 'jquery'
describe "Keymap", ->
fragment = null
keymap = null
beforeEach ->
keymap = new Keymap
fragment = $ """
<div class="command-mode">
<div class="child-node">
<div class="grandchild-node"/>
</div>
</div>
"""
describe ".handleKeyEvent(event)", ->
deleteCharHandler = null
insertCharHandler = null
beforeEach ->
keymap.bindKeys '.command-mode', 'x': 'deleteChar'
keymap.bindKeys '.insert-mode', 'x': 'insertChar'
deleteCharHandler = jasmine.createSpy 'deleteCharHandler'
insertCharHandler = jasmine.createSpy 'insertCharHandler'
fragment.on 'deleteChar', deleteCharHandler
fragment.on 'insertChar', insertCharHandler
it "adds a 'keystroke' string to the event object", ->
event = keydownEvent('x', altKey: true, metaKey: true)
keymap.handleKeyEvent(event)
expect(event.keystroke).toBe 'alt-meta-x'
describe "when no binding matches the event", ->
it "returns true, so the event continues to propagate", ->
expect(keymap.handleKeyEvent(keydownEvent('0', target: fragment[0]))).toBeTruthy()
describe "when the event's target node matches a selector with a matching binding", ->
it "triggers the command event associated with that binding on the target node and returns false", ->
result = keymap.handleKeyEvent(keydownEvent('x', target: fragment[0]))
expect(result).toBe(false)
expect(deleteCharHandler).toHaveBeenCalled()
expect(insertCharHandler).not.toHaveBeenCalled()
deleteCharHandler.reset()
fragment.removeClass('command-mode').addClass('insert-mode')
event = keydownEvent('x', target: fragment[0])
keymap.handleKeyEvent(event)
expect(deleteCharHandler).not.toHaveBeenCalled()
expect(insertCharHandler).toHaveBeenCalled()
commandEvent = insertCharHandler.argsForCall[0][0]
expect(commandEvent.keyEvent).toBe event
expect(event.keystroke).toBe 'x'
describe "when the event's target node *descends* from a selector with a matching binding", ->
it "triggers the command event associated with that binding on the target node and returns false", ->
target = fragment.find('.child-node')[0]
result = keymap.handleKeyEvent(keydownEvent('x', target: target))
expect(result).toBe(false)
expect(deleteCharHandler).toHaveBeenCalled()
expect(insertCharHandler).not.toHaveBeenCalled()
deleteCharHandler.reset()
fragment.removeClass('command-mode').addClass('insert-mode')
keymap.handleKeyEvent(keydownEvent('x', target: target))
expect(deleteCharHandler).not.toHaveBeenCalled()
expect(insertCharHandler).toHaveBeenCalled()
describe "when the event's target node descends from multiple nodes that match selectors with a binding", ->
it "only triggers bindings on selectors associated with the closest ancestor node", ->
keymap.bindKeys '.child-node', 'x': 'foo'
fooHandler = jasmine.createSpy 'fooHandler'
fragment.on 'foo', fooHandler
target = fragment.find('.grandchild-node')[0]
keymap.handleKeyEvent(keydownEvent('x', target: target))
expect(fooHandler).toHaveBeenCalled()
expect(deleteCharHandler).not.toHaveBeenCalled()
expect(insertCharHandler).not.toHaveBeenCalled()
describe "when the event bubbles to a node that matches multiple selectors", ->
describe "when the matching selectors differ in specificity", ->
it "triggers the binding for the most specific selector", ->
keymap.bindKeys 'div .child-node', 'x': 'foo'
keymap.bindKeys '.command-mode .child-node', 'x': 'baz'
keymap.bindKeys '.child-node', 'x': 'bar'
fooHandler = jasmine.createSpy 'fooHandler'
barHandler = jasmine.createSpy 'barHandler'
bazHandler = jasmine.createSpy 'bazHandler'
fragment.on 'foo', fooHandler
fragment.on 'bar', barHandler
fragment.on 'baz', bazHandler
target = fragment.find('.grandchild-node')[0]
keymap.handleKeyEvent(keydownEvent('x', target: target))
expect(fooHandler).not.toHaveBeenCalled()
expect(barHandler).not.toHaveBeenCalled()
expect(bazHandler).toHaveBeenCalled()
describe "when the matching selectors have the same specificity", ->
it "triggers the bindings for the most recently declared selector", ->
keymap.bindKeys '.child-node', 'x': 'foo', 'y': 'baz'
keymap.bindKeys '.child-node', 'x': 'bar'
fooHandler = jasmine.createSpy 'fooHandler'
barHandler = jasmine.createSpy 'barHandler'
bazHandler = jasmine.createSpy 'bazHandler'
fragment.on 'foo', fooHandler
fragment.on 'bar', barHandler
fragment.on 'baz', bazHandler
target = fragment.find('.grandchild-node')[0]
keymap.handleKeyEvent(keydownEvent('x', target: target))
expect(barHandler).toHaveBeenCalled()
expect(fooHandler).not.toHaveBeenCalled()
keymap.handleKeyEvent(keydownEvent('y', target: target))
expect(bazHandler).toHaveBeenCalled()
describe ".bindKeys(selector, fnOrMap)", ->
describe "when called with a selector and a hash", ->
it "normalizes the key patterns in the hash to put the modifiers in alphabetical order", ->
fooHandler = jasmine.createSpy('fooHandler')
fragment.on 'foo', fooHandler
keymap.bindKeys '*', 'ctrl-alt-delete': 'foo'
result = keymap.handleKeyEvent(keydownEvent('delete', ctrlKey: true, altKey: true, target: fragment[0]))
expect(result).toBe(false)
expect(fooHandler).toHaveBeenCalled()
fooHandler.reset()
keymap.bindKeys '*', 'ctrl-alt--': 'foo'
result = keymap.handleKeyEvent(keydownEvent('-', ctrlKey: true, altKey: true, target: fragment[0]))
expect(result).toBe(false)
expect(fooHandler).toHaveBeenCalled()
describe "when called with a selector and a function", ->
it "calls the given function when selector matches", ->
handler = jasmine.createSpy 'handler'
keymap.bindKeys '.child-node', handler
target = fragment.find('.grandchild-node')[0]
event = keydownEvent('y', target: target)
keymap.handleKeyEvent event
expect(handler).toHaveBeenCalledWith(event)
describe "when the function returns a command string", ->
it "triggers the command event on the target and stops propagating the event", ->
keymap.bindKeys '*', 'x': 'foo'
keymap.bindKeys '*', -> 'bar'
fooHandler = jasmine.createSpy('fooHandler')
barHandler = jasmine.createSpy('barHandler')
fragment.on 'foo', fooHandler
fragment.on 'bar', barHandler
target = fragment.find('.child-node')[0]
keymap.handleKeyEvent(keydownEvent('x', target: target))
expect(fooHandler).not.toHaveBeenCalled()
expect(barHandler).toHaveBeenCalled()
describe "when the function returns false", ->
it "stops propagating the event", ->
keymap.bindKeys '*', 'x': 'foo'
keymap.bindKeys '*', -> false
fooHandler = jasmine.createSpy('fooHandler')
fragment.on 'foo', fooHandler
target = fragment.find('.child-node')[0]
keymap.handleKeyEvent(keydownEvent('x', target: target))
expect(fooHandler).not.toHaveBeenCalled()
describe "when the function returns anything other than a string or false", ->
it "continues to propagate the event", ->
keymap.bindKeys '*', 'x': 'foo'
keymap.bindKeys '*', -> undefined
fooHandler = jasmine.createSpy('fooHandler')
fragment.on 'foo', fooHandler
target = fragment.find('.child-node')[0]
keymap.handleKeyEvent(keydownEvent('x', target: target))
expect(fooHandler).toHaveBeenCalled()
describe ".bindKey(selector, pattern, eventName)", ->
it "binds a single key", ->
keymap.bindKey '.child-node', 'z', 'foo'
fooHandler = jasmine.createSpy('fooHandler')
fragment.on 'foo', fooHandler
target = fragment.find('.child-node')[0]
keymap.handleKeyEvent(keydownEvent('z', target: target))
expect(fooHandler).toHaveBeenCalled()
describe ".keystrokeStringForEvent(event)", ->
describe "when no modifiers are pressed", ->
it "returns a string that identifies the key pressed", ->
expect(keymap.keystrokeStringForEvent(keydownEvent('a'))).toBe 'a'
expect(keymap.keystrokeStringForEvent(keydownEvent('['))).toBe '['
expect(keymap.keystrokeStringForEvent(keydownEvent('*'))).toBe '*'
expect(keymap.keystrokeStringForEvent(keydownEvent('left'))).toBe 'left'
expect(keymap.keystrokeStringForEvent(keydownEvent('\b'))).toBe 'backspace'
describe "when ctrl, alt or meta is pressed with a non-modifier key", ->
it "returns a string that identifies the key pressed", ->
expect(keymap.keystrokeStringForEvent(keydownEvent('a', altKey: true))).toBe 'alt-a'
expect(keymap.keystrokeStringForEvent(keydownEvent('[', metaKey: true))).toBe 'meta-['
expect(keymap.keystrokeStringForEvent(keydownEvent('*', ctrlKey: true))).toBe 'ctrl-*'
expect(keymap.keystrokeStringForEvent(keydownEvent('left', ctrlKey: true, metaKey: true, altKey: true))).toBe 'alt-ctrl-meta-left'
describe "when shift is pressed when a non-modifer key", ->
it "returns a string that identifies the key pressed", ->
expect(keymap.keystrokeStringForEvent(keydownEvent('A', shiftKey: true))).toBe 'A'
expect(keymap.keystrokeStringForEvent(keydownEvent('{', shiftKey: true))).toBe '{'
expect(keymap.keystrokeStringForEvent(keydownEvent('left', shiftKey: true))).toBe 'shift-left'
expect(keymap.keystrokeStringForEvent(keydownEvent('Left', shiftKey: true))).toBe 'shift-left'