diff options
| author | Stephen Blott | 2015-02-01 15:48:54 +0000 | 
|---|---|---|
| committer | Stephen Blott | 2015-02-01 15:48:54 +0000 | 
| commit | a9b8c4cdc176889c8ef442711109a9b5f4334c16 (patch) | |
| tree | be69020dd730339135510e54eebc450994d86f50 | |
| parent | 68e4aa3931babdf97c5f12dc5f655415e2073402 (diff) | |
| parent | 7939b69ffc2880ff4590d1c8dcfd5eb7492200fd (diff) | |
| download | vimium-a9b8c4cdc176889c8ef442711109a9b5f4334c16.tar.bz2 | |
Merge branch 'modes-rework-dom-tests'
| -rw-r--r-- | content_scripts/mode.coffee | 12 | ||||
| -rw-r--r-- | content_scripts/mode_insert.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 29 | ||||
| -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 | 
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' | 
