aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Blott2015-02-01 15:48:54 +0000
committerStephen Blott2015-02-01 15:48:54 +0000
commita9b8c4cdc176889c8ef442711109a9b5f4334c16 (patch)
treebe69020dd730339135510e54eebc450994d86f50
parent68e4aa3931babdf97c5f12dc5f655415e2073402 (diff)
parent7939b69ffc2880ff4590d1c8dcfd5eb7492200fd (diff)
downloadvimium-a9b8c4cdc176889c8ef442711109a9b5f4334c16.tar.bz2
Merge branch 'modes-rework-dom-tests'
-rw-r--r--content_scripts/mode.coffee12
-rw-r--r--content_scripts/mode_insert.coffee2
-rw-r--r--content_scripts/vimium_frontend.coffee29
-rw-r--r--lib/handler_stack.coffee5
-rw-r--r--tests/dom_tests/chrome.coffee5
-rw-r--r--tests/dom_tests/dom_tests.coffee542
-rw-r--r--tests/dom_tests/phantom_runner.coffee12
7 files changed, 232 insertions, 375 deletions
diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee
index acc3978e..42ea9930 100644
--- a/content_scripts/mode.coffee
+++ b/content_scripts/mode.coffee
@@ -169,14 +169,19 @@ class Mode
log: (args...) ->
console.log args... if @debug
- # Return the must-recently activated mode (only used in tests).
+ # For tests only.
@top: ->
@modes[@modes.length-1]
+ # For tests only.
+ @reset: ->
+ mode.exit() for mode in @modes
+ @modes = []
+
# BadgeMode is a pseudo mode for triggering badge updates on focus changes and state updates. It sits at the
# bottom of the handler stack, and so it receives state changes *after* all other modes, and can override the
-# badge choice of the other modes. We create the the one-and-only instance here.
-new class BadgeMode extends Mode
+# badge choice of the other modes.
+class BadgeMode extends Mode
constructor: () ->
super
name: "badge"
@@ -200,3 +205,4 @@ new class BadgeMode extends Mode
root = exports ? window
root.Mode = Mode
+root.BadgeMode = BadgeMode
diff --git a/content_scripts/mode_insert.coffee b/content_scripts/mode_insert.coffee
index eac4a3d0..6932f419 100644
--- a/content_scripts/mode_insert.coffee
+++ b/content_scripts/mode_insert.coffee
@@ -48,7 +48,7 @@ class InsertMode extends Mode
if @insertModeLock != event.target and DomUtils.isFocusable event.target
@activateOnElement event.target
- # Only for tests. This gives us a hook to test the status of the permanent instance.
+ # Only for tests. This gives us a hook to test the status of the permanently-installed instance.
InsertMode.permanentInstance = @ if @permanent
isActive: (event) ->
diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee
index 725d8a53..5d56ad5b 100644
--- a/content_scripts/vimium_frontend.coffee
+++ b/content_scripts/vimium_frontend.coffee
@@ -103,13 +103,8 @@ frameId = Math.floor(Math.random()*999999999)
hasModifiersRegex = /^<([amc]-)+.>/
-#
-# Complete initialization work that sould be done prior to DOMReady.
-#
-initializePreDomReady = ->
- settings.addEventListener("load", LinkHints.init.bind(LinkHints))
- settings.load()
-
+# Only exported for tests.
+window.initializeModes = ->
class NormalMode extends Mode
constructor: ->
super
@@ -122,12 +117,20 @@ initializePreDomReady = ->
# Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and
# activates/deactivates itself accordingly.
+ new BadgeMode
new NormalMode
new PassKeysMode
new InsertMode permanent: true
- checkIfEnabledForUrl()
+#
+# Complete initialization work that sould be done prior to DOMReady.
+#
+initializePreDomReady = ->
+ settings.addEventListener("load", LinkHints.init.bind(LinkHints))
+ settings.load()
+ initializeModes()
+ checkIfEnabledForUrl()
refreshCompletionKeys()
# Send the key to the key handler in the background page.
@@ -179,7 +182,7 @@ installListener = (element, event, callback) ->
# Run this as early as possible, so the page can't register any event handlers before us.
#
installedListeners = false
-initializeWhenEnabled = (newPassKeys) ->
+window.initializeWhenEnabled = (newPassKeys) ->
isEnabledForUrl = true
passKeys = newPassKeys
if (!installedListeners)
@@ -334,11 +337,9 @@ extend window,
focusInput: do ->
# Track the most recently focused input element.
recentlyFocusedElement = null
- handlerStack.push
- _name: "focus-input-tracker"
- focus: (event) ->
- recentlyFocusedElement = event.target if DomUtils.isEditable event.target
- true
+ window.addEventListener "focus",
+ (event) -> recentlyFocusedElement = event.target if DomUtils.isEditable event.target
+ , true
(count) ->
# Focus the first input element on the page, and create overlays to highlight all the input elements, with
diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee
index 76d835b7..3d635005 100644
--- a/lib/handler_stack.coffee
+++ b/lib/handler_stack.coffee
@@ -1,7 +1,6 @@
root = exports ? window
class HandlerStack
-
constructor: ->
@debug = false
@eventNumber = 0
@@ -95,5 +94,9 @@ class HandlerStack
label ||= if result then "continue/truthy" else "suppress"
console.log "#{@eventNumber}", type, handler._name, label
+ # For tests only.
+ reset: ->
+ @stack = []
+
root.HandlerStack = HandlerStack
root.handlerStack = new HandlerStack()
diff --git a/tests/dom_tests/chrome.coffee b/tests/dom_tests/chrome.coffee
index ad4ae74b..2e7c6a5a 100644
--- a/tests/dom_tests/chrome.coffee
+++ b/tests/dom_tests/chrome.coffee
@@ -3,6 +3,9 @@
#
root = exports ? window
+root.chromeMessages = []
+
+document.hasFocus = -> true
root.chrome = {
runtime: {
@@ -18,7 +21,7 @@ root.chrome = {
onMessage: {
addListener: ->
}
- sendMessage: ->
+ sendMessage: (message) -> chromeMessages.unshift message
getManifest: ->
getURL: (url) -> "../../#{url}"
}
diff --git a/tests/dom_tests/dom_tests.coffee b/tests/dom_tests/dom_tests.coffee
index a4713a72..11fbe11f 100644
--- a/tests/dom_tests/dom_tests.coffee
+++ b/tests/dom_tests/dom_tests.coffee
@@ -1,29 +1,38 @@
-#
-# Dispatching keyboard events via the DOM would require async tests,
-# which tend to be more complicated. Here we create mock events and
-# invoke the handlers directly.
-#
-mockKeyboardEvent = (keyChar) ->
- event = {}
- 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 = -> @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
+
+# Install frontend event handlers.
+initializeWhenEnabled()
+
+installListener = (element, event, callback) ->
+ element.addEventListener event, (-> callback.apply(this, arguments)), true
+
+# A count of the number of keyboard events received by the page (for the most recently-sent keystroke). E.g.,
+# we expect 3 if the keystroke is passed through (keydown, keypress, keyup), and 0 if it is suppressed.
+pageKeyboardEventCount = 0
+
+sendKeyboardEvent = (key) ->
+ pageKeyboardEventCount = 0
+ response = window.callPhantom
+ request: "keyboard"
+ key: key
+
+# These listeners receive events after the main frontend listeners, and do not receive suppressed events.
+for type in [ "keydown", "keypress", "keyup" ]
+ installListener window, type, (event) ->
+ pageKeyboardEventCount += 1
+
+# Some tests have side effects on the handler stack and the active mode, so these are reset on setup.
+initializeModeState = ->
+ Mode.reset()
+ handlerStack.reset()
+ initializeModes()
+ # We use "m" as the only mapped key, "p" as a passkey, and "u" as an unmapped key.
+ refreshCompletionKeys
+ completionKeys: "mp"
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: true
+ passKeys: "p"
+ handlerStack.bubbleEvent "registerKeyQueue",
+ keyQueue: ""
#
# Retrieve the hint markers as an array object.
@@ -40,6 +49,7 @@ createGeneralHintTests = (isFilteredMode) ->
context "Link hints",
setup ->
+ initializeModeState()
testContent = "<a>test</a>" + "<a>tress</a>"
document.getElementById("test-div").innerHTML = testContent
stub settings.values, "filterLinkHints", false
@@ -77,6 +87,7 @@ createGeneralHintTests true
context "Alphabetical link hints",
setup ->
+ initializeModeState()
stub settings.values, "filterLinkHints", false
stub settings.values, "linkHintCharacters", "ab"
@@ -99,7 +110,7 @@ context "Alphabetical link hints",
should "narrow the hints", ->
hintMarkers = getHintMarkers()
- LinkHints.onKeyDownInMode hintMarkers, mockKeyboardEvent("A")
+ sendKeyboardEvent "A"
assert.equal "none", hintMarkers[1].style.display
assert.equal "", hintMarkers[0].style.display
@@ -112,6 +123,7 @@ context "Filtered link hints",
context "Text hints",
setup ->
+ initializeModeState()
testContent = "<a>test</a>" + "<a>tress</a>" + "<a>trait</a>" + "<a>track<img alt='alt text'/></a>"
document.getElementById("test-div").innerHTML = testContent
LinkHints.init()
@@ -128,17 +140,18 @@ context "Filtered link hints",
should "narrow the hints", ->
hintMarkers = getHintMarkers()
- LinkHints.onKeyDownInMode hintMarkers, mockKeyboardEvent("T")
- LinkHints.onKeyDownInMode hintMarkers, mockKeyboardEvent("R")
+ sendKeyboardEvent "T"
+ sendKeyboardEvent "R"
assert.equal "none", hintMarkers[0].style.display
assert.equal "1", hintMarkers[1].hintString
assert.equal "", hintMarkers[1].style.display
- LinkHints.onKeyDownInMode hintMarkers, mockKeyboardEvent("A")
+ sendKeyboardEvent "A"
assert.equal "2", hintMarkers[3].hintString
context "Image hints",
setup ->
+ initializeModeState()
testContent = "<a><img alt='alt text'/></a><a><img alt='alt text' title='some title'/></a>
<a><img title='some title'/></a>" + "<a><img src='' width='320px' height='100px'/></a>"
document.getElementById("test-div").innerHTML = testContent
@@ -158,6 +171,7 @@ context "Filtered link hints",
context "Input hints",
setup ->
+ initializeModeState()
testContent = "<input type='text' value='some value'/><input type='password' value='some value'/>
<textarea>some text</textarea><label for='test-input'/>a label</label>
<input type='text' id='test-input' value='some value'/>
@@ -180,39 +194,40 @@ context "Filtered link hints",
context "Input focus",
setup ->
+ initializeModeState()
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", ->
+ should "focus the first element", ->
focusInput 1
assert.equal "first", document.activeElement.id
+ should "focus the nth element", ->
focusInput 100
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", ->
+ should "activate insert mode on the first element", ->
focusInput 1
- handlerStack.bubbleEvent 'focus', target: document.activeElement
assert.isTrue InsertMode.permanentInstance.isActive()
+ should "activate insert mode on the first element", ->
focusInput 100
- handlerStack.bubbleEvent 'focus', target: document. activeElement
assert.isTrue InsertMode.permanentInstance.isActive()
- should "select the previously-focused input when count is 1", ->
- focusInput 100
- handlerStack.bubbleEvent 'focus', target: document. activeElement
+ should "activate the most recently-selected input if the count is 1", ->
+ focusInput 3
focusInput 1
assert.equal "third", document.activeElement.id
+ should "not trigger insert if there are no inputs", ->
+ document.getElementById("test-div").innerHTML = ""
+ focusInput 1
+ assert.isFalse 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.
@@ -222,6 +237,7 @@ context "Input focus",
context "Find prev / next links",
setup ->
+ initializeModeState()
window.location.hash = ""
should "find exact matches", ->
@@ -278,185 +294,88 @@ createLinks = (n) ->
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()
+ initializeModeState()
should "suppress mapped keys", ->
- for event in [ "keydown", "keypress", "keyup" ]
- key = mockKeyboardEvent "m"
- handlerStack.bubbleEvent event, key
- assert.isTrue key.suppressed
+ sendKeyboardEvent "m"
+ assert.equal pageKeyboardEventCount, 0
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: ""
+ sendKeyboardEvent "u"
+ assert.equal pageKeyboardEventCount, 3
- handlerStack.bubbleEvent "registerKeyQueue",
- keyQueue: ""
+ should "not suppress escape", ->
+ sendKeyboardEvent "escape"
+ assert.equal pageKeyboardEventCount, 2
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
+ sendKeyboardEvent "p"
+ assert.equal pageKeyboardEventCount, 3
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
+ handlerStack.bubbleEvent "registerKeyQueue", keyQueue: "p"
+ sendKeyboardEvent "p"
+ assert.equal pageKeyboardEventCount, 0
context "Insert mode",
setup ->
- document.activeElement?.blur()
- backupStackState()
- refreshCompletionKeys
- completionKeys: "m"
-
- tearDown ->
- backupStackState()
+ initializeModeState()
+ @insertMode = new InsertMode global: true
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
+ sendKeyboardEvent "m"
+ assert.equal pageKeyboardEventCount, 3
+
+ should "exit on escape", ->
+ assert.isTrue @insertMode.modeIsActive
+ sendKeyboardEvent "escape"
+ assert.isFalse @insertMode.modeIsActive
+
+ should "resume normal mode after leaving insert mode", ->
+ @insertMode.exit()
+ sendKeyboardEvent "m"
+ assert.equal pageKeyboardEventCount, 0
context "Triggering insert mode",
setup ->
- document.activeElement?.blur()
- backupStackState()
- refreshCompletionKeys
- completionKeys: "m"
+ initializeModeState()
testContent = "<input type='text' id='first'/>
<input style='display:none;' id='second'/>
- <input type='password' id='third' value='some value'/>"
+ <input type='password' id='third' value='some value'/>
+ <p id='fourth' contenteditable='true'/>
+ <p id='fifth'/>"
document.getElementById("test-div").innerHTML = testContent
tearDown ->
- restoreStackState()
+ document.activeElement?.blur()
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", ->
+ assert.isTrue Mode.top().name == "insert" and not Mode.top().isActive()
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", ->
+ assert.isTrue Mode.top().name == "insert" and not Mode.top().isActive()
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 }
+ should "trigger insert mode on focus of contentEditable elements", ->
+ assert.isTrue Mode.top().name == "insert" and not Mode.top().isActive()
+ document.getElementById("fourth").focus()
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
-
+ should "not trigger insert mode on other elements", ->
+ assert.isTrue Mode.top().name == "insert" and not Mode.top().isActive()
+ document.getElementById("fifth").focus()
+ assert.isTrue Mode.top().name == "insert" and not Mode.top().isActive()
context "Mode utilities",
setup ->
- backupStackState()
- refreshCompletionKeys
- completionKeys: "m"
+ initializeModeState()
testContent = "<input type='text' id='first'/>
<input style='display:none;' id='second'/>
@@ -464,237 +383,152 @@ context "Mode utilities",
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()
+ 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 = 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"
+ test = new Mode exitOnEscape: true
- assert.isTrue Mode.top().name == "test"
- handlerStack.bubbleEvent "keydown", escape
- assert.isTrue Mode.top().name != "test"
+ assert.isTrue test.modeIsActive
+ sendKeyboardEvent "escape"
+ assert.equal pageKeyboardEventCount, 0
+ assert.isFalse test.modeIsActive
should "not exit on escape if not enabled", ->
- escape =
- keyCode: 27
- keyIdentifier: ""
- stopImmediatePropagation: ->
-
- new Mode
- exitOnEscape: false
- name: "test"
+ test = new Mode exitOnEscape: false
- assert.isTrue Mode.top().name == "test"
- handlerStack.bubbleEvent "keydown", escape
- assert.isTrue Mode.top().name == "test"
+ assert.isTrue test.modeIsActive
+ sendKeyboardEvent "escape"
+ assert.equal pageKeyboardEventCount, 2
+ assert.isTrue test.modeIsActive
should "exit on blur", ->
element = document.getElementById("first")
element.focus()
+ test = new Mode exitOnBlur: element
- 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()
+ assert.isTrue test.modeIsActive
+ element.blur()
+ assert.isFalse test.modeIsActive
- new Mode
- exitOnBlur: null
- name: "test"
+ should "not exit on blur if not enabled", ->
+ element = document.getElementById("first")
+ element.focus()
+ test = new Mode exitOnBlur: false
- assert.isTrue Mode.top().name == "test"
- handlerStack.bubbleEvent "blur", { target: element }
- assert.isTrue Mode.top().name == "test"
+ assert.isTrue test.modeIsActive
+ element.blur()
+ assert.isTrue test.modeIsActive
should "register state change", ->
- enabled = null
- passKeys = null
+ test = new Mode trackState: true
+ handlerStack.bubbleEvent "registerStateChange", { enabled: "one", passKeys: "two" }
- class Test extends Mode
- constructor: ->
- super
- trackState: true
+ assert.isTrue test.enabled == "one"
+ assert.isTrue test.passKeys == "two"
- registerStateChange: ->
- enabled = @enabled
- passKeys = @passKeys
-
- new Test()
- handlerStack.bubbleEvent "registerStateChange",
- enabled: "enabled"
- passKeys: "passKeys"
- assert.isTrue enabled == "enabled"
- assert.isTrue passKeys == "passKeys"
+ should "register the keyQueue", ->
+ test = new Mode trackState: true
+ handlerStack.bubbleEvent "registerKeyQueue", keyQueue: "hello"
- 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
+ assert.isTrue test.keyQueue == "hello"
context "PostFindMode",
setup ->
- backupStackState()
- refreshCompletionKeys
- completionKeys: "m"
+ initializeModeState()
- testContent = "<input type='text' id='first'/>
- <input style='display:none;' id='second'/>
- <input type='password' id='third' value='some value'/>"
+ testContent = "<input type='text' id='first'/>"
document.getElementById("test-div").innerHTML = testContent
-
- @escape =
- keyCode: 27
- keyIdentifier: ""
- stopImmediatePropagation: ->
- preventDefault: ->
-
- @element = document.getElementById("first")
- @element.focus()
- handlerStack.bubbleEvent "focus", { target: document.activeElement }
+ document.getElementById("first").focus()
+ @postFindMode = new PostFindMode
tearDown ->
- restoreStackState()
document.getElementById("test-div").innerHTML = ""
should "be a singleton", ->
- count = 0
+ assert.isTrue @postFindMode.modeIsActive
+ new PostFindMode
+ assert.isFalse @postFindMode.modeIsActive
- 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 "suppress unmapped printable keys", ->
+ sendKeyboardEvent "m"
+ assert.equal pageKeyboardEventCount, 0
- should "enter insert mode on immediate escape", ->
+ should "be deactivated on click events", ->
+ handlerStack.bubbleEvent "click", target: document.activeElement
+ assert.isFalse @postFindMode.modeIsActive
- new PostFindMode @element
- assert.isTrue Mode.top().name == "post-find"
- handlerStack.bubbleEvent "keydown", @escape
- assert.isTrue Mode.top().name == "insert"
+ should "enter insert mode on immediate escape", ->
+ sendKeyboardEvent "escape"
+ assert.equal pageKeyboardEventCount, 0
+ assert.isFalse @postFindMode.modeIsActive
- 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"
+ should "not enter insert mode on subsequent escapes", ->
+ sendKeyboardEvent "a"
+ sendKeyboardEvent "escape"
+ assert.isTrue @postFindMode.modeIsActive
context "Mode badges",
setup ->
- backupStackState()
+ initializeModeState()
+ testContent = "<input type='text' id='first'/>"
+ document.getElementById("test-div").innerHTML = testContent
tearDown ->
- restoreStackState()
+ document.getElementById("test-div").innerHTML = ""
- should "have no badge without passKeys", ->
- handlerStack.bubbleEvent "registerStateChange",
- enabled: true
- passKeys: ""
+ should "have no badge in normal mode", ->
+ Mode.updateBadge()
+ assert.isTrue chromeMessages[0].badge == ""
- handlerStack.bubbleEvent "updateBadge", badge = { badge: "" }
- assert.isTrue badge.badge == ""
+ should "have an I badge in insert mode by focus", ->
+ document.getElementById("first").focus()
+ assert.isTrue chromeMessages[0].badge == "I"
- should "have no badge with passKeys", ->
- handlerStack.bubbleEvent "registerStateChange",
- enabled: true
- passKeys: "p"
+ should "have no badge after leaving insert mode by focus", ->
+ document.getElementById("first").focus()
+ document.getElementById("first").blur()
+ assert.isTrue chromeMessages[0].badge == ""
+
+ should "have an I badge in global insert mode", ->
+ new InsertMode global: true
+ assert.isTrue chromeMessages[0].badge == "I"
+
+ should "have no badge after leaving global insert mode", ->
+ mode = new InsertMode global: true
+ mode.exit()
+ assert.isTrue chromeMessages[0].badge == ""
- handlerStack.bubbleEvent "updateBadge", badge = { badge: "" }
- assert.isTrue badge.badge == ""
+ should "have a ? badge in PostFindMode (immediately)", ->
+ document.getElementById("first").focus()
+ new PostFindMode
+ assert.isTrue chromeMessages[0].badge == "?"
+
+ should "have no badge in PostFindMode (subsequently)", ->
+ document.getElementById("first").focus()
+ new PostFindMode
+ sendKeyboardEvent "a"
+ assert.isTrue chromeMessages[0].badge == ""
should "have no badge when disabled", ->
handlerStack.bubbleEvent "registerStateChange",
enabled: false
passKeys: ""
- new InsertMode()
- handlerStack.bubbleEvent "updateBadge", badge = { badge: "" }
- assert.isTrue badge.badge == ""
+ document.getElementById("first").focus()
+ assert.isTrue chromeMessages[0].badge == ""
diff --git a/tests/dom_tests/phantom_runner.coffee b/tests/dom_tests/phantom_runner.coffee
index d05d9ab4..93218724 100644
--- a/tests/dom_tests/phantom_runner.coffee
+++ b/tests/dom_tests/phantom_runner.coffee
@@ -14,13 +14,23 @@ page.onConsoleMessage = (msg) ->
console.log msg
page.onError = (msg, trace) ->
- console.log(msg);
+ console.log(msg)
trace.forEach (item) ->
console.log(' ', item.file, ':', item.line)
page.onResourceError = (resourceError) ->
console.log(resourceError.errorString)
+page.onCallback = (request) ->
+ switch request.request
+ when "keyboard"
+ switch request.key
+ when "escape"
+ page.sendEvent "keydown", page.event.key.Escape
+ page.sendEvent "keyup", page.event.key.Escape
+ else
+ page.sendEvent "keypress", request.key
+
testfile = path.join(path.dirname(system.args[0]), 'dom_tests.html')
page.open testfile, (status) ->
if status != 'success'