From 26ff39d355f7d405eeb1b1394afbb677fa01e4a2 Mon Sep 17 00:00:00 2001
From: Stephen Blott
Date: Mon, 19 Jan 2015 05:39:24 +0000
Subject: Rework DOM tests.
- Set up modes such that they can be re-initialised.
- Move initialisation of BadgeMode to general initialisation function.
- Add reset() method for handlerStack.
- Consistently use initializeModeState() in all tests' setup().
- Refactor focusInput tests.
- Add some more tests.
- Simplify some other tests.
Note: Clean-up of the inputFocus overlay now happens when the exit()
method is called in Mode.reset(). This eliminates most needs to
artificially bubble a keyboard event to clear the overlay.
---
content_scripts/mode.coffee | 12 +-
content_scripts/vimium_frontend.coffee | 21 ++-
lib/handler_stack.coffee | 3 +
tests/dom_tests/chrome.coffee | 5 +-
tests/dom_tests/dom_tests.coffee | 297 +++++++++++++++------------------
5 files changed, 167 insertions(+), 171 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/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee
index fdd36ab9..643f9c6b 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)
diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee
index 76d835b7..b8049b81 100644
--- a/lib/handler_stack.coffee
+++ b/lib/handler_stack.coffee
@@ -95,5 +95,8 @@ class HandlerStack
label ||= if result then "continue/truthy" else "suppress"
console.log "#{@eventNumber}", type, handler._name, label
+ 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 33177c59..d8b499f2 100644
--- a/tests/dom_tests/dom_tests.coffee
+++ b/tests/dom_tests/dom_tests.coffee
@@ -12,18 +12,22 @@ mockKeyboardEvent = (keyChar) ->
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
+# Some tests have side effects on the handler stack and the active mode, so these are reset as necessary.
+initializeModeState = ->
+ Mode.reset()
+ handlerStack.reset()
+ initializeModes()
+ # We use "m" as the only mapped key, "p" as a passkey (sometimes), and "u" as an unmapped key.
+ refreshCompletionKeys
+ completionKeys: "mp"
+ handlerStack.bubbleEvent "registerStateChange",
+ enabled: true
+ passKeys: ""
+ handlerStack.bubbleEvent "registerKeyQueue",
+ keyQueue: ""
+
+# Install event handlers.
+initializeWhenEnabled()
#
# Retrieve the hint markers as an array object.
@@ -40,6 +44,7 @@ createGeneralHintTests = (isFilteredMode) ->
context "Link hints",
setup ->
+ initializeModeState()
testContent = "test" + "tress"
document.getElementById("test-div").innerHTML = testContent
stub settings.values, "filterLinkHints", false
@@ -77,6 +82,7 @@ createGeneralHintTests true
context "Alphabetical link hints",
setup ->
+ initializeModeState()
stub settings.values, "filterLinkHints", false
stub settings.values, "linkHintCharacters", "ab"
@@ -112,6 +118,7 @@ context "Filtered link hints",
context "Text hints",
setup ->
+ initializeModeState()
testContent = "test" + "tress" + "trait" + "track"
document.getElementById("test-div").innerHTML = testContent
LinkHints.init()
@@ -139,6 +146,7 @@ context "Filtered link hints",
context "Image hints",
setup ->
+ initializeModeState()
testContent = "
" + "
"
document.getElementById("test-div").innerHTML = testContent
@@ -158,6 +166,7 @@ context "Filtered link hints",
context "Input hints",
setup ->
+ initializeModeState()
testContent = "
a label
@@ -180,33 +189,38 @@ context "Filtered link hints",
context "Input focus",
setup ->
+ initializeModeState()
testContent = "
"
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", ->
+ assert.isTrue Mode.top().name == "insert" and not Mode.top().isActive()
focusInput 1
- handlerStack.bubbleEvent 'focus', target: document.activeElement
assert.isTrue InsertMode.permanentInstance.isActive()
+ should "activate insert mode on the first element", ->
+ assert.isTrue Mode.top().name == "insert" and not Mode.top().isActive()
focusInput 100
- handlerStack.bubbleEvent 'focus', target: document. activeElement
assert.isTrue InsertMode.permanentInstance.isActive()
+ should "not trigger insert if there are no inputs", ->
+ assert.isTrue Mode.top().name == "insert" and not Mode.top().isActive()
+ document.getElementById("test-div").innerHTML = ""
+ focusInput 1
+ assert.isTrue Mode.top().name == "insert" and not Mode.top().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.
@@ -216,6 +230,7 @@ context "Input focus",
context "Find prev / next links",
setup ->
+ initializeModeState()
window.location.hash = ""
should "find exact matches", ->
@@ -275,19 +290,14 @@ createLinks = (n) ->
# 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
+ for k in [ "m", "p" ]
+ for event in [ "keydown", "keypress", "keyup" ]
+ key = mockKeyboardEvent "p"
+ handlerStack.bubbleEvent event, key
+ assert.isTrue key.suppressed
should "not suppress unmapped keys", ->
for event in [ "keydown", "keypress", "keyup" ]
@@ -297,67 +307,29 @@ context "Normal mode",
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.
+ initializeModeState()
handlerStack.bubbleEvent "registerStateChange",
enabled: true
passKeys: "p"
- # Then verify passKey.
+ should "not suppress passKeys, but suppress other mapped keys", ->
+ # 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.
+ # Verify 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"
+ keyQueue: "p"
- # Now verify that the key is suppressed.
+ # Verify that the passKey is indeed now suppressed.
for event in [ "keydown", "keypress", "keyup" ]
key = mockKeyboardEvent "p"
handlerStack.bubbleEvent event, key
@@ -365,13 +337,7 @@ context "Passkeys mode",
context "Insert mode",
setup ->
- document.activeElement?.blur()
- backupStackState()
- refreshCompletionKeys
- completionKeys: "m"
-
- tearDown ->
- backupStackState()
+ initializeModeState()
should "not suppress mapped keys in insert mode", ->
# First verify normal-mode key (just to verify the framework).
@@ -381,8 +347,7 @@ context "Insert mode",
assert.isTrue key.suppressed
# Install insert mode.
- insertMode = new InsertMode
- global: true
+ insertMode = new InsertMode global: true
# Then verify insert mode.
for event in [ "keydown", "keypress", "keyup" ]
@@ -400,57 +365,53 @@ context "Insert mode",
context "Triggering insert mode",
setup ->
- document.activeElement?.blur()
- backupStackState()
- refreshCompletionKeys
- completionKeys: "m"
+ initializeModeState()
testContent = "
- "
+
+