diff options
| -rw-r--r-- | Cakefile | 3 | ||||
| -rw-r--r-- | background_scripts/main.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/link_hints.coffee | 6 | ||||
| -rw-r--r-- | test_harnesses/automated.html | 312 | ||||
| -rw-r--r-- | test_harnesses/automated/automated.coffee | 197 | ||||
| -rw-r--r-- | test_harnesses/automated/automated.html | 49 |
6 files changed, 252 insertions, 317 deletions
@@ -8,7 +8,8 @@ spawn_with_opts = (proc_name, opts) -> opt_array.push "--#{key}=#{value}" spawn proc_name, opt_array -src_directories = ["tests", "background_scripts", "content_scripts", "lib", "options"] +src_directories = ["tests", "background_scripts", "content_scripts", "lib", "options", + "test_harnesses/automated"] task "build", "compile all coffeescript files to javascript", -> coffee = spawn "coffee", ["-c"].concat(src_directories) diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 5b1ed4f7..c392f967 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -558,7 +558,7 @@ sendRequestHandlers = refreshCompleter: refreshCompleter # Convenience function for development use. -window.runTests = -> open(chrome.extension.getURL('test_harnesses/automated.html')) +window.runTests = -> open(chrome.extension.getURL('test_harnesses/automated/automated.html')) # # Begin initialization. diff --git a/content_scripts/link_hints.coffee b/content_scripts/link_hints.coffee index 5cdbc6a7..1d5d668a 100644 --- a/content_scripts/link_hints.coffee +++ b/content_scripts/link_hints.coffee @@ -178,9 +178,9 @@ LinkHints = else if (linksMatched.length == 1) @activateLink(linksMatched[0], delay) else - for i, marker of hintMarkers + for marker in hintMarkers @hideMarker(marker) - for i, matched of linksMatched + for matched in linksMatched @showMarker(matched, @markerMatcher.hintKeystrokeQueue.length) false # We've handled this key, so prevent propagation. @@ -404,7 +404,7 @@ filterHints = if (event.keyCode == keyCodes.enter) # activate the lowest-numbered link hint that is visible for marker in hintMarkers - if (marker.style.display != "none") + if (marker.style.display != "none") return { linksMatched: [ marker ] } else if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) # backspace clears hint key queue first, then acts on link text key queue. diff --git a/test_harnesses/automated.html b/test_harnesses/automated.html deleted file mode 100644 index 4c5f08ab..00000000 --- a/test_harnesses/automated.html +++ /dev/null @@ -1,312 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" - "http://www.w3.org/TR/html4/strict.dtd"> -<html> - <head> - <style type="text/css"> - body { - font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; - width: 800px; - margin: 0px auto; - } - #output-div { - white-space: pre-wrap; - background-color: #eee; - font-family: monospace; - margin: 0 0 50px 0; - border-style: dashed; - border-width: 1px 1px 0 1px; - border-color: #999; - } - .errorPosition { - color: #f33; - font-weight: bold; - } - .output-section { - padding: 10px 15px 10px 15px; - border-bottom: dashed 1px #999; - } - </style> - <link rel="stylesheet" type="text/css" href="../vimium.css" /> - <script type="text/javascript" src="../lib/utils.js"></script> - <script type="text/javascript" src="../lib/keyboard_utils.js"></script> - <script type="text/javascript" src="../lib/dom_utils.js"></script> - <script type="text/javascript" src="../content_scripts/link_hints.js"></script> - <script type="text/javascript" src="../lib/clipboard.js"></script> - <script type="text/javascript" src="../content_scripts/vimium_frontend.js"></script> - <script type="text/javascript" src="../tests/shoulda.js/shoulda.js"></script> - <script type="text/javascript"> - /* - * 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. - */ - function mockKeyboardEvent(keyChar) { - var event = {}; - event.charCode = keyCodes[keyChar] !== undefined ? keyCodes[keyChar] : keyChar.charCodeAt(0); - event.keyIdentifier = "U+00" + event.charCode.toString(16); - event.keyCode = event.charCode; - event.stopPropagation = function(){}; - event.preventDefault = function(){}; - return event; - } - - /* - * Generate tests that are common to both default and filtered - * link hinting modes. - */ - function createGeneralHintTests(isFilteredMode) { - context("Link hints", - - setup(function() { - var testContent = - "<a>test</a>" + - "<a>tress</a>"; - document.getElementById("test-div").innerHTML = testContent; - LinkHints.markerMatcher = alphabetHints; - }), - - tearDown(function() { - document.getElementById("test-div").innerHTML = ""; - }), - - should("create hints when activated, discard them when deactivated", function() { - LinkHints.activateMode(); - assert.isFalse(LinkHints.hintMarkerContainingDiv == null); - LinkHints.deactivateMode(); - assert.isTrue(LinkHints.hintMarkerContainingDiv == null); - }), - - should("position items correctly", function() { - function 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(); - assertStartPosition(document.getElementsByTagName("a")[0], LinkHints.hintMarkers[0]); - assertStartPosition(document.getElementsByTagName("a")[1], LinkHints.hintMarkers[1]); - LinkHints.deactivateMode(); - - stub(document.body.style, "position", "relative"); - LinkHints.activateMode(); - assertStartPosition(document.getElementsByTagName("a")[0], LinkHints.hintMarkers[0]); - assertStartPosition(document.getElementsByTagName("a")[1], LinkHints.hintMarkers[1]); - LinkHints.deactivateMode(); - }) - - ); - } - createGeneralHintTests(false); - createGeneralHintTests(true); - - context("Alphabetical link hints", - - setup(function() { - stub(settings.values, "filterLinkHints", "false"); - stub(settings.values, "linkHintCharacters", "ab") - LinkHints.markerMatcher = alphabetHints; - - // Three hints will trigger double hint chars. - createLinks(3); - LinkHints.activateMode(); - }), - - tearDown(function() { - LinkHints.deactivateMode(); - document.getElementById("test-div").innerHTML = ""; - }), - - should("label the hints correctly", function() { - // TODO(philc): This test verifies the current behavior, but the current behavior is incorrect. - // The output here should be something like aa, ab, b. - var expectedHints = ["aa", "ba", "ab"]; - console.log(LinkHints.hintMarkers); - for (var i = 0; i < expectedHints.length; i++) - assert.equal(expectedHints[i], LinkHints.hintMarkers[i].hintString); - }), - - should("narrow the hints", function() { - LinkHints.onKeyDownInMode(mockKeyboardEvent("A")); - assert.equal("none", LinkHints.hintMarkers[1].style.display); - assert.equal("", LinkHints.hintMarkers[0].style.display); - }) - - ); - - context("Filtered link hints", - - setup(function() { - stub(settings.values, "filterLinkHints", "true"); - LinkHints.markerMatcher = filterHints; - }), - - context("Text hints", - - setup(function() { - var testContent = - "<a>test</a>" + - "<a>tress</a>" + - "<a>trait</a>" + - "<a>track<img alt='alt text'/></a>"; - document.getElementById("test-div").innerHTML = testContent; - LinkHints.activateMode(); - }), - - tearDown(function() { - document.getElementById("test-div").innerHTML = ""; - LinkHints.deactivateMode(); - }), - - should("label the hints", function() { - for (var i = 0; i < 4; i++) - assert.equal((i + 1).toString(), LinkHints.hintMarkers[i].textContent.toLowerCase()); - }), - - should("narrow the hints", function() { - LinkHints.onKeyDownInMode(mockKeyboardEvent("T")); - LinkHints.onKeyDownInMode(mockKeyboardEvent("R")); - assert.equal("none", LinkHints.hintMarkers[0].style.display); - assert.equal("1", LinkHints.hintMarkers[1].hintString); - assert.equal("", LinkHints.hintMarkers[1].style.display); - LinkHints.onKeyDownInMode(mockKeyboardEvent("A")); - assert.equal("2", LinkHints.hintMarkers[3].hintString); - }) - - ), - - context("Image hints", - - setup(function() { - var 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; - LinkHints.activateMode(); - }), - - tearDown(function() { - document.getElementById("test-div").innerHTML = ""; - LinkHints.deactivateMode(); - }), - - should("label the images", function() { - assert.equal("1: alt text", LinkHints.hintMarkers[0].textContent.toLowerCase()); - assert.equal("2: alt text", LinkHints.hintMarkers[1].textContent.toLowerCase()); - assert.equal("3: some title", LinkHints.hintMarkers[2].textContent.toLowerCase()); - assert.equal("4", LinkHints.hintMarkers[3].textContent.toLowerCase()); - }) - - ), - - context("Input hints", - - setup(function() { - var 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'/>" + - "<label for='test-input-2'/>a label: </label><input type='text' id='test-input-2' value='some value'/>"; - document.getElementById("test-div").innerHTML = testContent; - LinkHints.activateMode(); - }), - - tearDown(function() { - document.getElementById("test-div").innerHTML = ""; - LinkHints.deactivateMode(); - }), - - should("label the input elements", function() { - assert.equal("1", LinkHints.hintMarkers[0].textContent.toLowerCase()); - assert.equal("2", LinkHints.hintMarkers[1].textContent.toLowerCase()); - assert.equal("3", LinkHints.hintMarkers[2].textContent.toLowerCase()); - assert.equal("4: a label", LinkHints.hintMarkers[3].textContent.toLowerCase()); - assert.equal("5: a label", LinkHints.hintMarkers[4].textContent.toLowerCase()); - }) - - ), - - context("Input focus", - - setup(function() { - var 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(function() { - document.getElementById("test-div").innerHTML = ""; - }), - - should("focus the right element", function() { - focusInput(1); - assert.equal('first', document.activeElement.id); - focusInput(100); - assert.equal('third', document.activeElement.id); - }) - ) - - ); - - context("Input focus", - - setup(function() { - var 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(function() { - document.getElementById("test-div").innerHTML = ""; - }), - - should("focus the right element", function() { - focusInput(1); - assert.equal('first', document.activeElement.id); - focusInput(100); - assert.equal('third', document.activeElement.id); - }) - - ); - - Tests.outputMethod = function(output) { - var newOutput = Array.prototype.join.call(arguments, "\n"); - newOutput = newOutput.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); // escape html - // highlight the source of the error - newOutput = newOutput.replace(/\/([^:/]+):([0-9]+):([0-9]+)/, - "/<span class='errorPosition'>$1:$2</span>:$3"); - document.getElementById("output-div").innerHTML += - "<div class='output-section'>" + newOutput + "</div>"; - console.log.apply(console, arguments); - } - - // ensure the extension has time to load before commencing the tests - document.addEventListener("DOMContentLoaded", function() { - setTimeout(Tests.run, 200); - }); - - function createLinks(n) { - for (var i = 0; i < n; i++) { - var link = document.createElement("a"); - link.textContent = "test"; - document.getElementById("test-div").appendChild(link); - } - } - </script> - </head> - <body> - <!-- should always be the first element on the page --> - <div id="test-div"></div> - - <h1>Vimium Tests</h1> - - <div id="output-div"></div> - - </body> -</html> diff --git a/test_harnesses/automated/automated.coffee b/test_harnesses/automated/automated.coffee new file mode 100644 index 00000000..c6f7639a --- /dev/null +++ b/test_harnesses/automated/automated.coffee @@ -0,0 +1,197 @@ +# +# 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 = "<a>test</a>" + "<a>tress</a>" + 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 = "<a>test</a>" + "<a>tress</a>" + "<a>trait</a>" + "<a>track<img alt='alt text'/></a>" + 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 = "<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 + 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 = "<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'/>" + "<label for='test-input-2'/>a label: </label><input type='text' id='test-input-2' value='some value'/>" + 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 = "<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 -> + 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, "<").replace(/>/g, ">") + # highlight the source of the error + newOutput = newOutput.replace(/\/([^:/]+):([0-9]+):([0-9]+)/, "/<span class='errorPosition'>$1:$2</span>:$3") + document.getElementById("output-div").innerHTML += "<div class='output-section'>" + newOutput + "</div>" + console.log.apply console, args + +document.addEventListener "DOMContentLoaded", -> + setTimeout Tests.run, 200 + +# ensure the extension has time to load before commencing the tests +createLinks = (n) -> + for i in [0...n] by 1 + link = document.createElement("a") + link.textContent = "test" + document.getElementById("test-div").appendChild link diff --git a/test_harnesses/automated/automated.html b/test_harnesses/automated/automated.html new file mode 100644 index 00000000..a0c8c7de --- /dev/null +++ b/test_harnesses/automated/automated.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> + <head> + <style type="text/css"> + body { + font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; + width: 800px; + margin: 0px auto; + } + #output-div { + white-space: pre-wrap; + background-color: #eee; + font-family: monospace; + margin: 0 0 50px 0; + border-style: dashed; + border-width: 1px 1px 0 1px; + border-color: #999; + } + .errorPosition { + color: #f33; + font-weight: bold; + } + .output-section { + padding: 10px 15px 10px 15px; + border-bottom: dashed 1px #999; + } + </style> + <link rel="stylesheet" type="text/css" href="../../vimium.css" /> + <script type="text/javascript" src="../../lib/utils.js"></script> + <script type="text/javascript" src="../../lib/keyboard_utils.js"></script> + <script type="text/javascript" src="../../lib/dom_utils.js"></script> + <script type="text/javascript" src="../../lib/clipboard.js"></script> + <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/vimium_frontend.js"></script> + <script type="text/javascript" src="../../tests/shoulda.js/shoulda.js"></script> + <script type="text/javascript" src="automated.js"></script> + </head> + <body> + <!-- should always be the first element on the page --> + <div id="test-div"></div> + + <h1>Vimium Tests</h1> + + <div id="output-div"></div> + + </body> +</html> |
