#
# 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.stopPropagation = ->
  event.preventDefault = ->
  event
#
# Retrieve the hint markers as an array object.
# 
getHintMarkers = ->
  Array::slice.call document.getElementsByClassName("vimiumHintMarker"), 0
#
# Generate tests that are common to both default and filtered
# link hinting modes.
# 
createGeneralHintTests = (isFilteredMode) ->
  context "Link hints",
    setup ->
      testContent = "test" + "tress"
      document.getElementById("test-div").innerHTML = testContent
      stub settings.values, "filterLinkHints", false
    tearDown ->
      document.getElementById("test-div").innerHTML = ""
    should "create hints when activated, discard them when deactivated", ->
      LinkHints.activateMode()
      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", "static"
      LinkHints.activateMode()
      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.activateMode()
      hintMarkers = getHintMarkers()
      assertStartPosition document.getElementsByTagName("a")[0], hintMarkers[0]
      assertStartPosition document.getElementsByTagName("a")[1], hintMarkers[1]
      LinkHints.deactivateMode()
createGeneralHintTests false
createGeneralHintTests true
context "Alphabetical link hints",
  setup ->
    stub settings.values, "filterLinkHints", false
    stub settings.values, "linkHintCharacters", "ab"
    # Three hints will trigger double hint chars.
    createLinks 3
    LinkHints.init()
    LinkHints.activateMode()
  tearDown ->
    LinkHints.deactivateMode()
    document.getElementById("test-div").innerHTML = ""
  should "label the hints correctly", ->
    # TODO(philc): This test verifies the current behavior, but the current behavior is incorrect.
    # The output here should be something like aa, ab, b.
    hintMarkers = getHintMarkers()
    expectedHints = ["aa", "ba", "ab"]
    for hint, i in expectedHints
      assert.equal hint, hintMarkers[i].hintString
  should "narrow the hints", ->
    hintMarkers = getHintMarkers()
    LinkHints.onKeyDownInMode hintMarkers, mockKeyboardEvent("A")
    assert.equal "none", hintMarkers[1].style.display
    assert.equal "", hintMarkers[0].style.display
context "Filtered link hints",
  setup ->
    stub settings.values, "filterLinkHints", true
  context "Text hints",
    setup ->
      testContent = "test" + "tress" + "trait" + "track"
      document.getElementById("test-div").innerHTML = testContent
      LinkHints.init()
      LinkHints.activateMode()
    tearDown ->
      document.getElementById("test-div").innerHTML = ""
      LinkHints.deactivateMode()
    should "label the hints", ->
      hintMarkers = getHintMarkers()
      for i in [0...4]
        assert.equal (i + 1).toString(), hintMarkers[i].textContent.toLowerCase()
    should "narrow the hints", ->
      hintMarkers = getHintMarkers()
      LinkHints.onKeyDownInMode hintMarkers, mockKeyboardEvent("T")
      LinkHints.onKeyDownInMode hintMarkers, mockKeyboardEvent("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")
      assert.equal "2", hintMarkers[3].hintString
  context "Image hints",
    setup ->
      testContent = "
" + "
" + "
" + "
"
      document.getElementById("test-div").innerHTML = testContent
      LinkHints.activateMode()
    tearDown ->
      document.getElementById("test-div").innerHTML = ""
      LinkHints.deactivateMode()
    should "label the images", ->
      hintMarkers = getHintMarkers()
      assert.equal "1: alt text", hintMarkers[0].textContent.toLowerCase()
      assert.equal "2: alt text", hintMarkers[1].textContent.toLowerCase()
      assert.equal "3: some title", hintMarkers[2].textContent.toLowerCase()
      assert.equal "4", hintMarkers[3].textContent.toLowerCase()
  context "Input hints",
    setup ->
      testContent = "" + "" + "" + "a label" + "a label: "
      document.getElementById("test-div").innerHTML = testContent
      LinkHints.activateMode()
    tearDown ->
      document.getElementById("test-div").innerHTML = ""
      LinkHints.deactivateMode()
    should "label the input elements", ->
      hintMarkers = getHintMarkers()
      assert.equal "1", hintMarkers[0].textContent.toLowerCase()
      assert.equal "2", hintMarkers[1].textContent.toLowerCase()
      assert.equal "3", hintMarkers[2].textContent.toLowerCase()
      assert.equal "4: a label", hintMarkers[3].textContent.toLowerCase()
      assert.equal "5: a label", hintMarkers[4].textContent.toLowerCase()
context "Input focus",
  setup ->
    testContent = "" + "" + ""
    document.getElementById("test-div").innerHTML = testContent
  tearDown ->
    document.getElementById("test-div").innerHTML = ""
  should "focus the right element", ->
    focusInput 1
    assert.equal "first", document.activeElement.id
    # deactivate the tabbing mode and its overlays
    handlerStack[handlerStack.length - 1].keydown mockKeyboardEvent("A")
    focusInput 100
    assert.equal "third", document.activeElement.id
    handlerStack[handlerStack.length - 1].keydown mockKeyboardEvent("A")
Tests.outputMethod = (args...) ->
  newOutput = args.join "\n"
  # escape html
  newOutput = newOutput.replace(/&/g, "&").replace(//g, ">")
  # highlight the source of the error
  newOutput = newOutput.replace(/\/([^:/]+):([0-9]+):([0-9]+)/, "/$1:$2:$3")
  document.getElementById("output-div").innerHTML += "