diff options
Diffstat (limited to 'tests/dom_tests/dom_tests.coffee')
| -rw-r--r-- | tests/dom_tests/dom_tests.coffee | 451 |
1 files changed, 448 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 == "" + |
