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