aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorStephen Blott2015-01-18 10:39:09 +0000
committerStephen Blott2015-01-18 10:39:09 +0000
commita1edae57e2847c2b6ffcae60ea8c9c16216e4692 (patch)
tree30ff186038028f9d0c0d5cc08d572ca56dda8819 /tests
parent8c9e429074580ea20aba662ee430d87bd73ebc4b (diff)
parent5d087c89917e21872711b7b908fcdd3c7e9e7f17 (diff)
downloadvimium-a1edae57e2847c2b6ffcae60ea8c9c16216e4692.tar.bz2
Merge pull request #1413 from smblott-github/modes
A modal-browsing framework
Diffstat (limited to 'tests')
-rw-r--r--tests/dom_tests/dom_tests.coffee451
-rw-r--r--tests/dom_tests/dom_tests.html5
-rw-r--r--tests/unit_tests/handler_stack_test.coffee23
-rw-r--r--tests/unit_tests/test_chrome_stubs.coffee2
4 files changed, 478 insertions, 3 deletions
diff --git a/tests/dom_tests/dom_tests.coffee b/tests/dom_tests/dom_tests.coffee
index 4a61877c..c73e0885 100644
--- a/tests/dom_tests/dom_tests.coffee
+++ b/tests/dom_tests/dom_tests.coffee
@@ -8,10 +8,23 @@ mockKeyboardEvent = (keyChar) ->
event.charCode = (if keyCodes[keyChar] isnt undefined then keyCodes[keyChar] else keyChar.charCodeAt(0))
event.keyIdentifier = "U+00" + event.charCode.toString(16)
event.keyCode = event.charCode
- event.stopImmediatePropagation = ->
- event.preventDefault = ->
+ event.stopImmediatePropagation = -> @suppressed = true
+ event.preventDefault = -> @suppressed = true
event
+# Some of these tests have side effects on the handler stack and active mode. Therefore, we take backups and
+# restore them on tear down.
+backupStackState = ->
+ Mode.backup = Mode.modes[..]
+ InsertMode.permanentInstance.exit()
+ handlerStack.backup = handlerStack.stack[..]
+restoreStackState = ->
+ for mode in Mode.modes
+ mode.exit() unless mode in Mode.backup
+ Mode.modes = Mode.backup
+ InsertMode.permanentInstance.exit()
+ handlerStack.stack = handlerStack.backup
+
#
# Retrieve the hint markers as an array object.
#
@@ -170,9 +183,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
+ backupStackState()
tearDown ->
document.getElementById("test-div").innerHTML = ""
+ restoreStackState()
should "focus the right element", ->
focusInput 1
@@ -184,6 +199,16 @@ context "Input focus",
assert.equal "third", document.activeElement.id
handlerStack.bubbleEvent 'keydown', mockKeyboardEvent("A")
+ # This is the same as above, but also verifies that focusInput activates insert mode.
+ should "activate insert mode", ->
+ focusInput 1
+ handlerStack.bubbleEvent 'focus', { target: document.activeElement }
+ assert.isTrue InsertMode.permanentInstance.isActive()
+
+ focusInput 100
+ handlerStack.bubbleEvent 'focus', { target: document. activeElement }
+ assert.isTrue InsertMode.permanentInstance.isActive()
+
# TODO: these find prev/next link tests could be refactored into unit tests which invoke a function which has
# a tighter contract than goNext(), since they test minor aspects of goNext()'s link matching behavior, and we
# don't need to construct external state many times over just to test that.
@@ -243,9 +268,429 @@ context "Find prev / next links",
goNext()
assert.equal '#first', window.location.hash
-
createLinks = (n) ->
for i in [0...n] by 1
link = document.createElement("a")
link.textContent = "test"
document.getElementById("test-div").appendChild link
+
+# For these tests, we use "m" as a mapped key, "p" as a pass key, and "u" as an unmapped key.
+context "Normal mode",
+ setup ->
+ document.activeElement?.blur()
+ backupStackState()
+ refreshCompletionKeys
+ completionKeys: "m"
+
+ tearDown ->
+ restoreStackState()
+
+ should "suppress mapped keys", ->
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "m"
+ handlerStack.bubbleEvent event, key
+ assert.isTrue key.suppressed
+
+ should "not suppress unmapped keys", ->
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "u"
+ handlerStack.bubbleEvent event, key
+ assert.isFalse key.suppressed
+
+context "Passkeys mode",
+ setup ->
+ backupStackState()
+ refreshCompletionKeys
+ completionKeys: "mp"
+
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: true
+ passKeys: ""
+
+ handlerStack.bubbleEvent "registerKeyQueue",
+ keyQueue: ""
+
+ tearDown ->
+ restoreStackState()
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: true
+ passKeys: ""
+
+ handlerStack.bubbleEvent "registerKeyQueue",
+ keyQueue: ""
+
+ should "not suppress passKeys", ->
+ # First check normal-mode key (just to verify the framework).
+ for k in [ "m", "p" ]
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "p"
+ handlerStack.bubbleEvent event, key
+ assert.isTrue key.suppressed
+
+ # Install passKey.
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: true
+ passKeys: "p"
+
+ # Then verify passKey.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "p"
+ handlerStack.bubbleEvent event, key
+ assert.isFalse key.suppressed
+
+ # And re-verify a mapped key.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "m"
+ handlerStack.bubbleEvent event, key
+ assert.isTrue key.suppressed
+
+ should "suppress passKeys with a non-empty keyQueue", ->
+ # Install passKey.
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: true
+ passKeys: "p"
+
+ # First check the key is indeed not suppressed.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "p"
+ handlerStack.bubbleEvent event, key
+ assert.isFalse key.suppressed
+
+ handlerStack.bubbleEvent "registerKeyQueue",
+ keyQueue: "1"
+
+ # Now verify that the key is suppressed.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "p"
+ handlerStack.bubbleEvent event, key
+ assert.isTrue key.suppressed
+
+context "Insert mode",
+ setup ->
+ document.activeElement?.blur()
+ backupStackState()
+ refreshCompletionKeys
+ completionKeys: "m"
+
+ tearDown ->
+ backupStackState()
+
+ should "not suppress mapped keys in insert mode", ->
+ # First verify normal-mode key (just to verify the framework).
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "m"
+ handlerStack.bubbleEvent event, key
+ assert.isTrue key.suppressed
+
+ # Install insert mode.
+ insertMode = new InsertMode
+ global: true
+
+ # Then verify insert mode.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "m"
+ handlerStack.bubbleEvent event, key
+ assert.isFalse key.suppressed
+
+ insertMode.exit()
+
+ # Then verify that insert mode has been successfully removed.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "m"
+ handlerStack.bubbleEvent event, key
+ assert.isTrue key.suppressed
+
+context "Triggering insert mode",
+ setup ->
+ document.activeElement?.blur()
+ backupStackState()
+ refreshCompletionKeys
+ completionKeys: "m"
+
+ 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.top().name == "insert" and Mode.top().isActive()
+
+ should "trigger insert mode on focus of text input", ->
+ document.getElementById("first").focus()
+ handlerStack.bubbleEvent "focus", { target: document.activeElement }
+
+ assert.isTrue Mode.top().name == "insert" and Mode.top().isActive()
+
+ should "trigger insert mode on focus of password input", ->
+ document.getElementById("third").focus()
+ handlerStack.bubbleEvent "focus", { target: document.activeElement }
+
+ assert.isTrue Mode.top().name == "insert" and Mode.top().isActive()
+
+ should "not handle suppressed events", ->
+ document.getElementById("first").focus()
+ handlerStack.bubbleEvent "focus", { target: document.activeElement }
+ assert.isTrue Mode.top().name == "insert" and Mode.top().isActive()
+
+ for event in [ "keydown", "keypress", "keyup" ]
+ # Because "m" is mapped, we expect insert mode to ignore it, and normal mode to suppress it.
+ key = mockKeyboardEvent "m"
+ InsertMode.suppressEvent key
+ handlerStack.bubbleEvent event, key
+ assert.isTrue key.suppressed
+
+
+context "Mode utilities",
+ setup ->
+ backupStackState()
+ refreshCompletionKeys
+ completionKeys: "m"
+
+ 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 "not have duplicate singletons", ->
+ count = 0
+
+ class Test extends Mode
+ constructor: ->
+ count += 1
+ super
+ singleton: Test
+
+ exit: ->
+ count -= 1
+ super()
+
+ assert.isTrue count == 0
+ for [1..10]
+ mode = new Test(); assert.isTrue count == 1
+
+ mode.exit()
+ assert.isTrue count == 0
+
+ should "exit on escape", ->
+ escape =
+ keyCode: 27
+
+ new Mode
+ exitOnEscape: true
+ name: "test"
+
+ assert.isTrue Mode.top().name == "test"
+ handlerStack.bubbleEvent "keydown", escape
+ assert.isTrue Mode.top().name != "test"
+
+ should "not exit on escape if not enabled", ->
+ escape =
+ keyCode: 27
+ keyIdentifier: ""
+ stopImmediatePropagation: ->
+
+ new Mode
+ exitOnEscape: false
+ name: "test"
+
+ assert.isTrue Mode.top().name == "test"
+ handlerStack.bubbleEvent "keydown", escape
+ assert.isTrue Mode.top().name == "test"
+
+ should "exit on blur", ->
+ element = document.getElementById("first")
+ element.focus()
+
+ new Mode
+ exitOnBlur: element
+ name: "test"
+
+ assert.isTrue Mode.top().name == "test"
+ handlerStack.bubbleEvent "blur", { target: element }
+ assert.isTrue Mode.top().name != "test"
+
+ should "not exit on blur if not enabled", ->
+ element = document.getElementById("first")
+ element.focus()
+
+ new Mode
+ exitOnBlur: null
+ name: "test"
+
+ assert.isTrue Mode.top().name == "test"
+ handlerStack.bubbleEvent "blur", { target: element }
+ assert.isTrue Mode.top().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")
+ element.focus()
+ handlerStack.bubbleEvent "focus", { target: document.activeElement }
+
+ # Verify that a key is not suppressed.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "u"
+ handlerStack.bubbleEvent event, key
+ assert.isFalse key.suppressed
+
+ new PostFindMode {}
+
+ # Verify that the key is now suppressed for keypress.
+ key = mockKeyboardEvent "u"
+ handlerStack.bubbleEvent "keypress",
+ extend key,
+ srcElement: element
+ assert.isTrue key.suppressed
+
+ # Verify key is not suppressed with Control key.
+ key = mockKeyboardEvent "u"
+ handlerStack.bubbleEvent "keypress",
+ extend key,
+ srcElement: element
+ ctrlKey: true
+ assert.isFalse key.suppressed
+
+ # Verify key is not suppressed with Meta key.
+ key = mockKeyboardEvent "u"
+ handlerStack.bubbleEvent "keypress",
+ extend key,
+ srcElement: element
+ metaKey: true
+ assert.isFalse key.suppressed
+
+context "PostFindMode",
+ setup ->
+ backupStackState()
+ refreshCompletionKeys
+ completionKeys: "m"
+
+ 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
+
+ @escape =
+ keyCode: 27
+ keyIdentifier: ""
+ stopImmediatePropagation: ->
+ preventDefault: ->
+
+ @element = document.getElementById("first")
+ @element.focus()
+ handlerStack.bubbleEvent "focus", { target: document.activeElement }
+
+ tearDown ->
+ restoreStackState()
+ document.getElementById("test-div").innerHTML = ""
+
+ should "be a singleton", ->
+ count = 0
+
+ assert.isTrue Mode.top().name == "insert"
+ new PostFindMode @element
+ assert.isTrue Mode.top().name == "post-find"
+ new PostFindMode @element
+ assert.isTrue Mode.top().name == "post-find"
+ Mode.top().exit()
+ assert.isTrue Mode.top().name == "insert"
+
+ should "suppress unmapped printable keypress events", ->
+ # Verify key is passed through.
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "u"
+ handlerStack.bubbleEvent event, key
+ assert.isFalse key.suppressed
+
+ new PostFindMode @element
+
+ # Verify key is now suppressed for keypress.
+ key = mockKeyboardEvent "u"
+ handlerStack.bubbleEvent "keypress",
+ extend key,
+ srcElement: @element
+ assert.isTrue key.suppressed
+
+ should "be clickable to focus", ->
+ new PostFindMode @element
+
+ assert.isTrue Mode.top().name != "insert"
+ handlerStack.bubbleEvent "click", { target: document.activeElement }
+ assert.isTrue Mode.top().name == "insert"
+
+ should "enter insert mode on immediate escape", ->
+
+ new PostFindMode @element
+ assert.isTrue Mode.top().name == "post-find"
+ handlerStack.bubbleEvent "keydown", @escape
+ assert.isTrue Mode.top().name == "insert"
+
+ should "not enter insert mode on subsequent escape", ->
+ new PostFindMode @element
+ assert.isTrue Mode.top().name == "post-find"
+ handlerStack.bubbleEvent "keydown", mockKeyboardEvent "u"
+ handlerStack.bubbleEvent "keydown", @escape
+ assert.isTrue Mode.top().name == "post-find"
+
+context "Mode badges",
+ setup ->
+ backupStackState()
+
+ tearDown ->
+ restoreStackState()
+
+ should "have no badge without passKeys", ->
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: true
+ passKeys: ""
+
+ handlerStack.bubbleEvent "updateBadge", badge = { badge: "" }
+ assert.isTrue badge.badge == ""
+
+ should "have no badge with passKeys", ->
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: true
+ passKeys: "p"
+
+ handlerStack.bubbleEvent "updateBadge", badge = { badge: "" }
+ assert.isTrue badge.badge == ""
+
+ should "have no badge when disabled", ->
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: false
+ passKeys: ""
+
+ new InsertMode()
+ handlerStack.bubbleEvent "updateBadge", badge = { badge: "" }
+ assert.isTrue badge.badge == ""
+
diff --git a/tests/dom_tests/dom_tests.html b/tests/dom_tests/dom_tests.html
index a764b42d..33759abd 100644
--- a/tests/dom_tests/dom_tests.html
+++ b/tests/dom_tests/dom_tests.html
@@ -39,6 +39,11 @@
<script type="text/javascript" src="../../content_scripts/link_hints.js"></script>
<script type="text/javascript" src="../../content_scripts/vomnibar.js"></script>
<script type="text/javascript" src="../../content_scripts/scroller.js"></script>
+ <script type="text/javascript" src="../../content_scripts/mode.js"></script>
+ <script type="text/javascript" src="../../content_scripts/mode_passkeys.js"></script>
+ <script type="text/javascript" src="../../content_scripts/mode_insert.js"></script>
+ <script type="text/javascript" src="../../content_scripts/mode_find.js"></script>
+ <script type="text/javascript" src="../../content_scripts/mode_visual.js"></script>
<script type="text/javascript" src="../../content_scripts/vimium_frontend.js"></script>
<script type="text/javascript" src="../shoulda.js/shoulda.js"></script>
diff --git a/tests/unit_tests/handler_stack_test.coffee b/tests/unit_tests/handler_stack_test.coffee
index 0ed8f4c0..0ed85e63 100644
--- a/tests/unit_tests/handler_stack_test.coffee
+++ b/tests/unit_tests/handler_stack_test.coffee
@@ -23,6 +23,29 @@ context "handlerStack",
assert.isTrue @handler2Called
assert.isFalse @handler1Called
+ should "terminate bubbling on stopBubblingAndTrue, and be true", ->
+ @handlerStack.push { keydown: => @handler1Called = true }
+ @handlerStack.push { keydown: => @handler2Called = true; @handlerStack.stopBubblingAndTrue }
+ assert.isTrue @handlerStack.bubbleEvent 'keydown', {}
+ assert.isTrue @handler2Called
+ assert.isFalse @handler1Called
+
+ should "terminate bubbling on stopBubblingAndTrue, and be false", ->
+ @handlerStack.push { keydown: => @handler1Called = true }
+ @handlerStack.push { keydown: => @handler2Called = true; @handlerStack.stopBubblingAndFalse }
+ assert.isFalse @handlerStack.bubbleEvent 'keydown', {}
+ assert.isTrue @handler2Called
+ assert.isFalse @handler1Called
+
+ should "restart bubbling on restartBubbling", ->
+ @handler1Called = 0
+ @handler2Called = 0
+ id = @handlerStack.push { keydown: => @handler1Called++; @handlerStack.remove(id); @handlerStack.restartBubbling }
+ @handlerStack.push { keydown: => @handler2Called++; true }
+ assert.isTrue @handlerStack.bubbleEvent 'keydown', {}
+ assert.isTrue @handler1Called == 1
+ assert.isTrue @handler2Called == 2
+
should "remove handlers correctly", ->
@handlerStack.push { keydown: => @handler1Called = true }
handlerId = @handlerStack.push { keydown: => @handler2Called = true }
diff --git a/tests/unit_tests/test_chrome_stubs.coffee b/tests/unit_tests/test_chrome_stubs.coffee
index 3258bcd6..7f666068 100644
--- a/tests/unit_tests/test_chrome_stubs.coffee
+++ b/tests/unit_tests/test_chrome_stubs.coffee
@@ -41,6 +41,8 @@ exports.chrome =
addListener: () -> true
getAll: () -> true
+ browserAction:
+ setBadgeBackgroundColor: ->
storage:
# chrome.storage.local
local: