diff options
| -rw-r--r-- | content_scripts/mode.coffee | 12 | ||||
| -rw-r--r-- | content_scripts/mode_insert.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/vimium.css | 4 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 40 | ||||
| -rw-r--r-- | lib/handler_stack.coffee | 5 | ||||
| -rw-r--r-- | tests/dom_tests/chrome.coffee | 5 | ||||
| -rw-r--r-- | tests/dom_tests/dom_tests.coffee | 542 | ||||
| -rw-r--r-- | tests/dom_tests/phantom_runner.coffee | 12 |
8 files changed, 241 insertions, 381 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.css b/content_scripts/vimium.css index e79f72d3..fb8824c2 100644 --- a/content_scripts/vimium.css +++ b/content_scripts/vimium.css @@ -222,11 +222,9 @@ div#vimiumHelpDialogFooter .toggleAdvancedCommands { div.vimiumHUD { display: block; position: fixed; - top: auto; - left: auto; bottom: 0px; color: black; - height: 13px; + height: auto; min-height: 13px; width: auto; max-width: 400px; diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index e1c703e6..a9e44ed2 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 @@ -352,7 +353,9 @@ extend window, continue if rect == null { element: element, rect: rect } - return if visibleInputs.length == 0 + if visibleInputs.length == 0 + HUD.showForDuration("There are no inputs to focus.", 1000) + return selectedInputIndex = if count == 1 @@ -760,12 +763,15 @@ handleEscapeForFindMode = -> window.getSelection().addRange(range) focusFoundLink() || selectFoundInputElement() +# Return true if character deleted, false otherwise. handleDeleteForFindMode = -> - if (findModeQuery.rawQuery.length == 0) + if findModeQuery.rawQuery.length == 0 exitFindMode() performFindInPlace() + false else updateQueryForFindMode findModeQuery.rawQuery.substring(0, findModeQuery.rawQuery.length - 1) + true # <esc> sends us into insert mode if possible, but <cr> does not. # <esc> corresponds approximately to 'nevermind, I have found it already' while <cr> means 'I want to save @@ -788,7 +794,7 @@ class FindMode extends Mode keydown: (event) => if event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey - handleDeleteForFindMode() + @exit() unless handleDeleteForFindMode() @suppressEvent else if event.keyCode == keyCodes.enter handleEnterForFindMode() 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' |
