aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Blott2015-01-11 07:15:20 +0000
committerStephen Blott2015-01-11 10:14:38 +0000
commite8f10007f1528808f72be6fac829cc55309527f2 (patch)
tree2311d31a869cb2b7bf32c857b0728eae1eb19589
parentd65075a3b66fae93a10b849162fa907d0eb99846 (diff)
downloadvimium-e8f10007f1528808f72be6fac829cc55309527f2.tar.bz2
Modes; add DOM tests.
-rw-r--r--content_scripts/mode.coffee4
-rw-r--r--lib/handler_stack.coffee8
-rw-r--r--tests/dom_tests/dom_tests.coffee309
3 files changed, 294 insertions, 27 deletions
diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee
index 46f5c3b7..6e40089e 100644
--- a/content_scripts/mode.coffee
+++ b/content_scripts/mode.coffee
@@ -44,7 +44,7 @@ count = 0
class Mode
# If Mode.debug is true, then we generate a trace of modes being activated and deactivated on the console, along
# with a list of the currently active modes.
- debug: false
+ debug: true
@modes: []
# Constants; short, readable names for handlerStack event-handler return values.
@@ -121,7 +121,7 @@ class Mode
keypress: (event) =>
@alwaysContinueBubbling =>
if event.srcElement == @options.suppressPrintableEvents
- if KeyboardUtils.isPrintable(event)
+ if KeyboardUtils.isPrintable event
event.vimium_suppress_event = true
Mode.updateBadge() if @badge
diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee
index c21ba8a8..b4cacf74 100644
--- a/lib/handler_stack.coffee
+++ b/lib/handler_stack.coffee
@@ -102,13 +102,5 @@ class HandlerStack
line = args.join " "
console.log line
- # Used by tests to get a duplicate copy of the initialized handler stack.
- duplicate: ->
- dup = new HandlerStack()
- dup.stack = @stack[..]
- for prop in [ "stopBubblingAndTrue", "stopBubblingAndFalse", "restartBubbling" ]
- dup[prop] = @[prop]
- dup
-
root.HandlerStack = HandlerStack
root.handlerStack = new HandlerStack()
diff --git a/tests/dom_tests/dom_tests.coffee b/tests/dom_tests/dom_tests.coffee
index ce54190a..c9fab56b 100644
--- a/tests/dom_tests/dom_tests.coffee
+++ b/tests/dom_tests/dom_tests.coffee
@@ -12,10 +12,16 @@ mockKeyboardEvent = (keyChar) ->
event.preventDefault = (event) -> @suppressed = true
event
-# Some of these tests have side effects on the handler stack. Therefore, we take backups of the stack, and
-# restore them later.
-backupHandlerStack = -> handlerStack.backup = handlerStack.stack
-restoreHandlerStack = -> handlerStack.stack = handlerStack.backup
+# Some of these tests have side effects on the handler stack and mode. Therefore, we take backups and restore
+# them later.
+backupStackState = ->
+ Mode.backup = Mode.modes[..]
+ handlerStack.backup = handlerStack.stack[..]
+restoreStackState = ->
+ for mode in Mode.modes
+ mode.exit() unless mode in Mode.backup
+ Mode.modes = Mode.backup
+ handlerStack.stack = handlerStack.backup
#
# Retrieve the hint markers as an array object.
@@ -175,11 +181,11 @@ context "Input focus",
testContent = "<input type='text' id='first'/><input style='display:none;' id='second'/>
<input type='password' id='third' value='some value'/>"
document.getElementById("test-div").innerHTML = testContent
- backupHandlerStack()
+ backupStackState()
tearDown ->
document.getElementById("test-div").innerHTML = ""
- restoreHandlerStack()
+ restoreStackState()
should "focus the right element", ->
focusInput 1
@@ -258,13 +264,13 @@ createLinks = (n) ->
context "Normal mode",
setup ->
- backupHandlerStack()
+ backupStackState()
refreshCompletionKeys
completionKeys: "o"
validFirstKeys: "o"
tearDown ->
- restoreHandlerStack()
+ restoreStackState()
should "suppress mapped keys", ->
for event in [ "keydown", "keypress", "keyup" ]
@@ -280,7 +286,7 @@ context "Normal mode",
context "Passkeys mode",
setup ->
- backupHandlerStack()
+ backupStackState()
refreshCompletionKeys
completionKeys: "oj"
validFirstKeys: "oj"
@@ -290,7 +296,7 @@ context "Passkeys mode",
passKeys: "j"
tearDown ->
- restoreHandlerStack()
+ restoreStackState()
handlerStack.bubbleEvent "registerStateChange",
enabled: true
passKeys: ""
@@ -310,13 +316,13 @@ context "Passkeys mode",
context "Insert mode",
setup ->
- backupHandlerStack()
+ backupStackState()
refreshCompletionKeys
completionKeys: "o"
validFirstKeys: "o"
tearDown ->
- backupHandlerStack()
+ backupStackState()
should "not suppress mapped keys in insert mode", ->
# First check normal-mode key (just to verify the framework).
@@ -336,21 +342,290 @@ context "Insert mode",
insertMode.exit()
- # Then check insert mode has been successfully removed.
+ # Then verify that insert mode has been successfully removed.
for event in [ "keydown", "keypress", "keyup" ]
key = mockKeyboardEvent "o"
handlerStack.bubbleEvent event, key
assert.isTrue key.suppressed
-context "Insert mode trigger",
+context "Insert-mode trigger",
setup ->
- backupHandlerStack()
+ backupStackState()
refreshCompletionKeys
completionKeys: "o"
validFirstKeys: "o"
+ testContent = "<input type='text' id='first'/>
+ <input style='display:none;' id='second'/>
+ <input type='password' id='third' value='some value'/>"
+ document.getElementById("test-div").innerHTML = testContent
+
+ tearDown ->
+ restoreStackState()
+ document.getElementById("test-div").innerHTML = ""
+
+ should "trigger insert mode on focus of contentEditable elements", ->
+ handlerStack.bubbleEvent "focus",
+ target:
+ isContentEditable: true
+
+ assert.isTrue Mode.modes[Mode.modes.length-1].name == "insert"
+
+ should "trigger insert mode on focus of text input", ->
+ document.getElementById("first").focus()
+ handlerStack.bubbleEvent "focus", { target: document.activeElement }
+
+ assert.isTrue Mode.modes[Mode.modes.length-1].name == "insert"
+
+ should "trigger insert mode on focus of password input", ->
+ document.getElementById("third").focus()
+ handlerStack.bubbleEvent "focus", { target: document.activeElement }
+
+ assert.isTrue Mode.modes[Mode.modes.length-1].name == "insert"
+
+ should "not trigger insert mode on focus of contentEditable elements", ->
+ new InsertModeBlocker()
+ handlerStack.bubbleEvent "focus",
+ target:
+ isContentEditable: true
+
+ assert.isTrue Mode.modes[Mode.modes.length-1].name != "insert"
+
+ should "not trigger insert mode on focus of text input", ->
+ new InsertModeBlocker()
+ document.getElementById("first").focus()
+ handlerStack.bubbleEvent "focus", { target: document.activeElement }
+
+ assert.isTrue Mode.modes[Mode.modes.length-1].name != "insert"
+
+ should "not trigger insert mode on focus of password input", ->
+ new InsertModeBlocker()
+ document.getElementById("third").focus()
+ handlerStack.bubbleEvent "focus", { target: document.activeElement }
+
+ assert.isTrue Mode.modes[Mode.modes.length-1].name != "insert"
+
+context "Mode utilities",
+ setup ->
+ backupStackState()
+ refreshCompletionKeys
+ completionKeys: "o"
+ validFirstKeys: "o"
+
+ testContent = "<input type='text' id='first'/>
+ <input style='display:none;' id='second'/>
+ <input type='password' id='third' value='some value'/>"
+ document.getElementById("test-div").innerHTML = testContent
+
tearDown ->
- backupHandlerStack()
+ restoreStackState()
+ document.getElementById("test-div").innerHTML = ""
+
+ should "not have duplicate singletons", ->
+ count = 0
+
+ class Test extends Mode
+ constructor: ->
+ count += 1
+ super
+ singleton: Test
+
+ exit: ->
+ count -= 1
+ super()
+
+ assert.isTrue count == 0
+ new Test()
+ assert.isTrue count == 1
+ new Test()
+ assert.isTrue count == 1
+
+ should "exit on escape", ->
+ escape =
+ keyCode: 27
+
+ new Mode
+ exitOnEscape: true
+ name: "test"
+
+ assert.isTrue Mode.modes[Mode.modes.length-1].name == "test"
+ handlerStack.bubbleEvent "keydown", escape
+ assert.isTrue Mode.modes[Mode.modes.length-1].name != "test"
+
+ should "exit on blur", ->
+ element = document.getElementById("first")
+
+ new Mode
+ exitOnBlur: element
+ name: "test"
+
+ assert.isTrue Mode.modes[Mode.modes.length-1].name == "test"
+ handlerStack.bubbleEvent "blur", { srcElement: element }
+ assert.isTrue Mode.modes[Mode.modes.length-1].name != "test"
+
+ should "register state change", ->
+ enabled = null
+ passKeys = null
+
+ class Test extends Mode
+ constructor: ->
+ super
+ trackState: true
+
+ registerStateChange: ->
+ enabled = @enabled
+ passKeys = @passKeys
+
+ new Test()
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: "enabled"
+ passKeys: "passKeys"
+ assert.isTrue enabled == "enabled"
+ assert.isTrue passKeys == "passKeys"
+
+ should "suppress printable keys", ->
+ element = document.getElementById("first")
+
+ # Verify key is always passed through.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "A"
+ handlerStack.bubbleEvent event, key
+ assert.isFalse key.suppressed
+
+ new Mode
+ suppressPrintableEvents: element
+
+ # Verify key is now suppressed for keypress.
+ key = mockKeyboardEvent "A"
+ handlerStack.bubbleEvent "keypress",
+ extend key,
+ srcElement: element
+ assert.isTrue key.suppressed
+
+ # Verify key is not suppressed with Control key.
+ key = mockKeyboardEvent "A"
+ handlerStack.bubbleEvent "keypress",
+ extend key,
+ srcElement: element
+ ctrlKey: true
+ assert.isFalse key.suppressed
+
+ # Verify key is not suppressed with Meta key.
+ key = mockKeyboardEvent "A"
+ handlerStack.bubbleEvent "keypress",
+ extend key,
+ srcElement: element
+ metaKey: true
+ assert.isFalse key.suppressed
+
+ # Verify other keyboard events are not suppressed.
+ key = mockKeyboardEvent "A"
+ handlerStack.bubbleEvent "keydown",
+ extend key,
+ srcElement: element
+ assert.isFalse key.suppressed
+
+context "PostFindMode",
+ setup ->
+ backupStackState()
+ refreshCompletionKeys
+ completionKeys: "o"
+ validFirstKeys: "o"
+
+ testContent = "<input type='text' id='first'/>
+ <input style='display:none;' id='second'/>
+ <input type='password' id='third' value='some value'/>"
+ document.getElementById("test-div").innerHTML = testContent
+
+ tearDown ->
+ restoreStackState()
+ document.getElementById("test-div").innerHTML = ""
+
+ should "be a singleton", ->
+ element = document.getElementById("first")
+ element.focus()
+ count = 0
+
+ class Test extends PostFindMode
+ constructor: (element) ->
+ count += 1
+ super element
+
+ exit: ->
+ count -= 1
+ super()
+
+ assert.isTrue count == 0
+ new Test element
+ assert.isTrue count == 1
+ new Test element
+ assert.isTrue count == 1
+
+ should "suppress unmapped printable keypress events", ->
+ element = document.getElementById("first")
+ element.focus()
+
+ # Verify key is passed through.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "A"
+ handlerStack.bubbleEvent event, key
+ assert.isFalse key.suppressed
- should "trigger insert mode on input-focus events", ->
+ new PostFindMode element
+
+ # Verify key is now suppressed for keypress.
+ key = mockKeyboardEvent "A"
+ handlerStack.bubbleEvent "keypress",
+ extend key,
+ srcElement: element
+ assert.isTrue key.suppressed
+
+ # Verify other keyboard events are not suppressed.
+ key = mockKeyboardEvent "A"
+ handlerStack.bubbleEvent "keydown",
+ extend key,
+ srcElement: element
+ assert.isFalse key.suppressed
+
+ # Verify keyboard events on other elements are not suppressed.
+ key = mockKeyboardEvent "A"
+ handlerStack.bubbleEvent "keypress",
+ extend key,
+ srcElement: document.body
+ assert.isFalse key.suppressed
+
+ should "be clickable to focus", ->
+ element = document.getElementById("first")
+ element.focus()
+
+ new PostFindMode element
+
+ assert.isTrue Mode.modes[Mode.modes.length-1].name != "insert"
+ handlerStack.bubbleEvent "click", { target: document.activeElement }
+ assert.isTrue Mode.modes[Mode.modes.length-1].name == "insert"
+
+ should "enter insert mode on immediate escape", ->
+ escape =
+ keyCode: 27
+ element = document.getElementById("first")
+ element.focus()
+
+ new PostFindMode element
+ assert.isTrue Mode.modes[Mode.modes.length-1].name != "insert"
+ handlerStack.bubbleEvent "keydown", escape
+ assert.isTrue Mode.modes[Mode.modes.length-1].name == "insert"
+
+ should "enter not insert mode on subsequent escape", ->
+ escape =
+ keyCode: 27
+ keyIdentifier: ""
+ stopImmediatePropagation: ->
+ element = document.getElementById("first")
+ element.focus()
+
+ new PostFindMode element
+ assert.isTrue Mode.modes[Mode.modes.length-1].name == "post-find"
+ handlerStack.bubbleEvent "keydown", mockKeyboardEvent "A"
+ handlerStack.bubbleEvent "keydown", escape
+ assert.isTrue Mode.modes[Mode.modes.length-1].name == "post-find"