window.vimiumDomTestsAreRunning = true # Install frontend event handlers. HUD.init() Frame.registerFrameId chromeFrameId: 0 getSelection = -> window.getSelection().toString() commandName = commandCount = null # Some tests have side effects on the handler stack and the active mode, so these are reset on setup. Also, # some tests affect the focus (e.g. Vomnibar tests), so we make sure the window has the focus. initializeModeState = -> window.focus() Mode.reset() handlerStack.reset() normalMode = installModes() normalMode.setPassKeys "p" normalMode.setKeyMapping m: options: {}, command: "m" # A mapped key. p: options: {}, command: "p" # A pass key. z: p: options: {}, command: "zp" # Not a pass key. normalMode.setCommandHandler ({command, count}) -> [commandName, commandCount] = [command.command, count] commandName = commandCount = null normalMode # Return this. # Tell Settings that it's been loaded. Settings.isLoaded = true # Shoulda.js doesn't support async code, so we try not to use any. Utils.nextTick = (func) -> func() # # Retrieve the hint markers as an array object. # getHintMarkers = -> Array::slice.call document.getElementsByClassName("vimiumHintMarker"), 0 stubSettings = (key, value) -> stub Settings.cache, key, JSON.stringify value HintCoordinator.sendMessage = (name, request = {}) -> HintCoordinator[name]? request; request activateLinkHintsMode = -> HintCoordinator.getHintDescriptors modeIndex: 0 HintCoordinator.activateMode hintDescriptors: {}, modeIndex: 0, originatingFrameId: frameId, # # Generate tests that are common to both default and filtered # link hinting modes. # createGeneralHintTests = (isFilteredMode) -> context "Link hints", setup -> initializeModeState() testContent = "test" + "tress" document.getElementById("test-div").innerHTML = testContent stubSettings "filterLinkHints", isFilteredMode stubSettings "linkHintCharacters", "ab" stubSettings "linkHintNumbers", "12" stub window, "windowIsFocused", -> true tearDown -> document.getElementById("test-div").innerHTML = "" should "create hints when activated, discard them when deactivated", -> linkHints = activateLinkHintsMode() assert.isFalse not linkHints.hintMarkerContainingDiv? linkHints.deactivateMode() assert.isTrue not linkHints.hintMarkerContainingDiv? should "position items correctly", -> assertStartPosition = (element1, element2) -> assert.equal element1.getClientRects()[0].left, element2.getClientRects()[0].left assert.equal element1.getClientRects()[0].top, element2.getClientRects()[0].top stub document.body.style, "position", "static" linkHints = activateLinkHintsMode() hintMarkers = getHintMarkers() assertStartPosition document.getElementsByTagName("a")[0], hintMarkers[0] assertStartPosition document.getElementsByTagName("a")[1], hintMarkers[1] linkHints.deactivateMode() stub document.body.style, "position", "relative" linkHints = activateLinkHintsMode() hintMarkers = getHintMarkers() assertStartPosition document.getElementsByTagName("a")[0], hintMarkers[0] assertStartPosition document.getElementsByTagName("a")[1], hintMarkers[1] linkHints.deactivateMode() createGeneralHintTests false createGeneralHintTests true context "False positives in link-hint", setup -> testContent = '' + '' document.getElementById("test-div").innerHTML = testContent stubSettings "filterLinkHints", true stubSettings "linkHintNumbers", "12" stub window, "windowIsFocused", -> true tearDown -> document.getElementById("test-div").innerHTML = "" should "handle false positives", -> linkHints = activateLinkHintsMode() hintMarkers = getHintMarkers() linkHints.deactivateMode() assert.equal 2, hintMarkers.length for hintMarker in hintMarkers assert.equal "clickable", hintMarker.linkText context "jsaction matching", setup -> stubSettings "filterLinkHints", true testContent = '
clickable
' document.getElementById("test-div").innerHTML = testContent @element = document.getElementById("test-paragraph") tearDown -> document.getElementById("test-div").innerHTML = "" should "select jsaction elements", -> for text in ["click:namespace.actionName", "namespace.actionName"] @element.setAttribute "jsaction", text linkHints = activateLinkHintsMode() hintMarkers = getHintMarkers().filter (marker) -> marker.linkText != "Frame." linkHints.deactivateMode() assert.equal 1, hintMarkers.length assert.equal "clickable", hintMarkers[0].linkText assert.equal @element, hintMarkers[0].localHintDescriptor.element should "not select inactive jsaction elements", -> for text in ["mousedown:namespace.actionName", "click:namespace._", "none", "namespace:_"] @element.setAttribute "jsaction", text linkHints = activateLinkHintsMode() hintMarkers = getHintMarkers().filter (marker) -> marker.linkText != "Frame." linkHints.deactivateMode() assert.equal 0, hintMarkers.length sendKeyboardEvent = (key, type="keydown", extra={}) -> handlerStack.bubbleEvent type, extend extra, type: type key: key preventDefault: -> stopImmediatePropagation: -> sendKeyboardEvents = (keys) -> sendKeyboardEvent key for key in keys.split "" inputs = [] context "Test link hints for focusing input elements correctly", setup -> initializeModeState() testDiv = document.getElementById("test-div") testDiv.innerHTML = "" stubSettings "filterLinkHints", false stubSettings "linkHintCharacters", "ab" # Every HTML5 input type except for hidden. We should be able to activate all of them with link hints. # # TODO: Re-insert "color" into the inputTypes list when PhantomJS issue #13979 is fixed and integrated. # Ref: https://github.com/ariya/phantomjs/issues/13979, and Vimium #1944. inputTypes = ["button", "checkbox", "date", "datetime", "datetime-local", "email", "file", "image", "month", "number", "password", "radio", "range", "reset", "search", "submit", "tel", "text", "time", "url", "week"] for type in inputTypes input = document.createElement "input" input.type = type testDiv.appendChild input inputs.push input # Manually add also a select element to test focus. input = document.createElement "select" testDiv.appendChild input inputs.push input tearDown -> document.getElementById("test-div").innerHTML = "" should "Focus each input when its hint text is typed", -> for input in inputs input.scrollIntoView() # Ensure the element is visible so we create a link hint for it. activeListener = ensureCalled (event) -> input.blur() if event.type == "focus" input.addEventListener "focus", activeListener, false input.addEventListener "click", activeListener, false activateLinkHintsMode() [hint] = getHintMarkers().filter (hint) -> input == HintCoordinator.getLocalHintMarker(hint.hintDescriptor).element sendKeyboardEvent char for char in hint.hintString input.removeEventListener "focus", activeListener, false input.removeEventListener "click", activeListener, false context "Test link hints for changing mode", setup -> initializeModeState() testDiv = document.getElementById("test-div") testDiv.innerHTML = "link" @linkHints = activateLinkHintsMode() tearDown -> document.getElementById("test-div").innerHTML = "" @linkHints.deactivateMode() should "change mode on shift", -> assert.equal "curr-tab", @linkHints.mode.name sendKeyboardEvent "Shift", "keydown" assert.equal "bg-tab", @linkHints.mode.name sendKeyboardEvent "Shift", "keyup" assert.equal "curr-tab", @linkHints.mode.name should "change mode on ctrl", -> assert.equal "curr-tab", @linkHints.mode.name sendKeyboardEvent "Control", "keydown" assert.equal "fg-tab", @linkHints.mode.name sendKeyboardEvent "Control", "keyup" assert.equal "curr-tab", @linkHints.mode.name context "Alphabetical link hints", setup -> initializeModeState() stubSettings "filterLinkHints", false stubSettings "linkHintCharacters", "ab" stub window, "windowIsFocused", -> true document.getElementById("test-div").innerHTML = "" # Three hints will trigger double hint chars. createLinks 3 @linkHints = activateLinkHintsMode() tearDown -> @linkHints.deactivateMode() document.getElementById("test-div").innerHTML = "" should "label the hints correctly", -> hintMarkers = getHintMarkers() expectedHints = ["aa", "b", "ab"] assert.equal 3, hintMarkers.length for hint, i in expectedHints assert.equal hint, hintMarkers[i].hintString should "narrow the hints", -> hintMarkers = getHintMarkers() sendKeyboardEvent "a" assert.equal "none", hintMarkers[1].style.display assert.equal "", hintMarkers[0].style.display should "generate the correct number of alphabet hints", -> alphabetHints = new AlphabetHints for n in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] hintStrings = alphabetHints.hintStrings n assert.equal n, hintStrings.length should "generate non-overlapping alphabet hints", -> alphabetHints = new AlphabetHints for n in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] hintStrings = alphabetHints.hintStrings n for h1 in hintStrings for h2 in hintStrings unless h1 == h2 assert.isFalse 0 == h1.indexOf h2 context "Filtered link hints", # Note. In all of these tests, the order of the elements returned by getHintMarkers() may be different from # the order they are listed in the test HTML content. This is because LinkHints.activateMode() sorts the # elements. setup -> stubSettings "filterLinkHints", true stubSettings "linkHintNumbers", "0123456789" stub window, "windowIsFocused", -> true context "Text hints", setup -> initializeModeState() testContent = "test" + "tress" + "trait" + "track
      It is an ancient Mariner,
      And he stoppeth one of three.
      By thy long grey beard and glittering eye,
      Now wherefore stopp'st thou me?
    
    """
    initializeModeState()
    @initialVisualMode = new VisualMode
  tearDown ->
    document.getElementById("test-div").innerHTML = ""
  should "enter caret mode", ->
    assert.isFalse @initialVisualMode.modeIsActive
    assert.equal "I", getSelection()
  should "exit caret mode on escape", ->
    sendKeyboardEvent "Escape", "keydown"
    assert.equal "", getSelection()
  should "move caret with l and h", ->
    assert.equal "I", getSelection()
    sendKeyboardEvent "l"
    assert.equal "t", getSelection()
    sendKeyboardEvent "h"
    assert.equal "I", getSelection()
  should "move caret with w and b", ->
    assert.equal "I", getSelection()
    sendKeyboardEvent "w"
    assert.equal "i", getSelection()
    sendKeyboardEvent "b"
    assert.equal "I", getSelection()
  should "move caret with e", ->
    assert.equal "I", getSelection()
    sendKeyboardEvent "e"
    assert.equal " ", getSelection()
    sendKeyboardEvent "e"
    assert.equal " ", getSelection()
  should "move caret with j and k", ->
    assert.equal "I", getSelection()
    sendKeyboardEvent "j"
    assert.equal "A", getSelection()
    sendKeyboardEvent "k"
    assert.equal "I", getSelection()
  should "re-use an existing selection", ->
    assert.equal "I", getSelection()
    sendKeyboardEvents "ww"
    assert.equal "a", getSelection()
    sendKeyboardEvent "Escape", "keydown"
    new VisualMode
    assert.equal "a", getSelection()
  should "not move the selection on caret/visual mode toggle", ->
    sendKeyboardEvents "ww"
    assert.equal "a", getSelection()
    for key in "vcvcvc".split()
      sendKeyboardEvent key
      assert.equal "a", getSelection()
context "Visual mode",
  setup ->
    document.getElementById("test-div").innerHTML = """
    
      It is an ancient Mariner,
      And he stoppeth one of three.
      By thy long grey beard and glittering eye,
      Now wherefore stopp'st thou me?
    
    """
    initializeModeState()
    @initialVisualMode = new VisualMode
    sendKeyboardEvent "w"
    sendKeyboardEvent "w"
    # We should now be at the "a" of "an".
    sendKeyboardEvent "v"
  tearDown ->
    document.getElementById("test-div").innerHTML = ""
  should "select word with e", ->
    assert.equal "a", getSelection()
    sendKeyboardEvent "e"
    assert.equal "an", getSelection()
    sendKeyboardEvent "e"
    assert.equal "an ancient", getSelection()
  should "select opposite end of the selection with o", ->
    assert.equal "a", getSelection()
    sendKeyboardEvent "e"
    assert.equal "an", getSelection()
    sendKeyboardEvent "e"
    assert.equal "an ancient", getSelection()
    sendKeyboardEvents "ow"
    assert.equal "ancient", getSelection()
    sendKeyboardEvents "oe"
    assert.equal "ancient Mariner", getSelection()
  should "accept a count", ->
    assert.equal "a", getSelection()
    sendKeyboardEvents "2e"
    assert.equal "an ancient", getSelection()
  should "select a word", ->
    assert.equal "a", getSelection()
    sendKeyboardEvents "aw"
    assert.equal "an", getSelection()
  should "select a word with a count", ->
    assert.equal "a", getSelection()
    sendKeyboardEvents "2aw"
    assert.equal "an ancient", getSelection()
  should "select a word with a count", ->
    assert.equal "a", getSelection()
    sendKeyboardEvents "2aw"
    assert.equal "an ancient", getSelection()
  should "select to start of line", ->
    assert.equal "a", getSelection()
    sendKeyboardEvents "0"
    assert.equal "It is", getSelection().trim()
  should "select to end of line", ->
    assert.equal "a", getSelection()
    sendKeyboardEvents "$"
    assert.equal "an ancient Mariner,", getSelection()
  should "re-enter caret mode", ->
    assert.equal "a", getSelection()
    sendKeyboardEvents "cww"
    assert.equal "M", getSelection()
context "Mode utilities",
  setup ->
    initializeModeState()
    testContent = "
      
      "
    document.getElementById("test-div").innerHTML = testContent
  tearDown ->
    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", ->
    test = new Mode exitOnEscape: true
    assert.isTrue test.modeIsActive
    sendKeyboardEvent "Escape", "keydown"
    assert.isFalse test.modeIsActive
  should "not exit on escape if not enabled", ->
    test = new Mode exitOnEscape: false
    assert.isTrue test.modeIsActive
    sendKeyboardEvent "Escape", "keydown"
    assert.isTrue test.modeIsActive
  should "exit on blur", ->
    element = document.getElementById("first")
    element.focus()
    test = new Mode exitOnBlur: element
    assert.isTrue test.modeIsActive
    element.blur()
    assert.isFalse test.modeIsActive
  should "not exit on blur if not enabled", ->
    element = document.getElementById("first")
    element.focus()
    test = new Mode exitOnBlur: false
    assert.isTrue test.modeIsActive
    element.blur()
    assert.isTrue test.modeIsActive
context "PostFindMode",
  setup ->
    initializeModeState()
    testContent = ""
    document.getElementById("test-div").innerHTML = testContent
    document.getElementById("first").focus()
    @postFindMode = new PostFindMode
  tearDown ->
    document.getElementById("test-div").innerHTML = ""
  should "be a singleton", ->
    assert.isTrue @postFindMode.modeIsActive
    new PostFindMode
    assert.isFalse @postFindMode.modeIsActive
  should "suppress unmapped printable keys", ->
    sendKeyboardEvent "a"
    assert.equal null, commandCount
  should "be deactivated on click events", ->
    handlerStack.bubbleEvent "click", target: document.activeElement
    assert.isFalse @postFindMode.modeIsActive
  should "enter insert mode on immediate escape", ->
    sendKeyboardEvent "Escape", "keydown"
    assert.equal null, commandCount
    assert.isFalse @postFindMode.modeIsActive
  should "not enter insert mode on subsequent escapes", ->
    sendKeyboardEvent "a"
    sendKeyboardEvent "Escape", "keydown"
    assert.isTrue @postFindMode.modeIsActive
context "WaitForEnter",
  setup ->
    initializeModeState()
    @isSuccess = null
    @waitForEnter = new WaitForEnter (isSuccess) => @isSuccess = isSuccess
  should "exit with success on Enter", ->
    assert.isTrue @waitForEnter.modeIsActive
    assert.isFalse @isSuccess?
    sendKeyboardEvent "Enter", "keydown"
    assert.isFalse @waitForEnter.modeIsActive
    assert.isTrue @isSuccess? and @isSuccess == true
  should "exit without success on Escape", ->
    assert.isTrue @waitForEnter.modeIsActive
    assert.isFalse @isSuccess?
    sendKeyboardEvent "Escape", "keydown"
    assert.isFalse @waitForEnter.modeIsActive
    assert.isTrue @isSuccess? and @isSuccess == false
  should "not exit on other keyboard events", ->
    assert.isTrue @waitForEnter.modeIsActive
    assert.isFalse @isSuccess?
    sendKeyboardEvents "abc"
    assert.isTrue @waitForEnter.modeIsActive
    assert.isFalse @isSuccess?
context "GrabBackFocus",
  setup ->
    testContent = ""
    document.getElementById("test-div").innerHTML = testContent
    stubSettings "grabBackFocus", true
  tearDown ->
    document.getElementById("test-div").innerHTML = ""
  should "blur an already focused input", ->
    document.getElementById("input").focus()
    assert.isTrue document.activeElement
    assert.isTrue DomUtils.isEditable document.activeElement
    initializeModeState()
    assert.isTrue document.activeElement
    assert.isFalse DomUtils.isEditable document.activeElement
  should "blur a newly focused input", ->
    initializeModeState()
    document.getElementById("input").focus()
    assert.isTrue document.activeElement
    assert.isFalse DomUtils.isEditable document.activeElement
  should "exit on a key event", ->
    initializeModeState()
    sendKeyboardEvent "a"
    document.getElementById("input").focus()
    assert.isTrue document.activeElement
    assert.isTrue DomUtils.isEditable document.activeElement
  should "exit on a mousedown event", ->
    initializeModeState()
    handlerStack.bubbleEvent "mousedown", target: document.body
    document.getElementById("input").focus()
    assert.isTrue document.activeElement
    assert.isTrue DomUtils.isEditable document.activeElement