From 6df16c591219d87058b4c48682d503382e44693f Mon Sep 17 00:00:00 2001 From: Jez Ng Date: Mon, 3 Sep 2012 23:19:35 -0400 Subject: Set up PhantomJS testing. --- Cakefile | 20 ++- background_scripts/main.coffee | 2 +- test_harnesses/automated/automated.coffee | 197 ----------------------------- test_harnesses/automated/automated.html | 49 -------- tests/completion_test.coffee | 165 ------------------------ tests/dom_tests/bind.js | 27 ++++ tests/dom_tests/chrome.coffee | 20 +++ tests/dom_tests/dom_tests.coffee | 200 ++++++++++++++++++++++++++++++ tests/dom_tests/dom_tests.html | 51 ++++++++ tests/dom_tests/phantom_runner.coffee | 32 +++++ tests/test_helper.coffee | 5 - tests/unit_tests/completion_test.coffee | 165 ++++++++++++++++++++++++ tests/unit_tests/test_helper.coffee | 5 + tests/unit_tests/utils_test.coffee | 20 +++ tests/utils_test.coffee | 20 --- 15 files changed, 536 insertions(+), 442 deletions(-) delete mode 100644 test_harnesses/automated/automated.coffee delete mode 100644 test_harnesses/automated/automated.html delete mode 100644 tests/completion_test.coffee create mode 100644 tests/dom_tests/bind.js create mode 100644 tests/dom_tests/chrome.coffee create mode 100644 tests/dom_tests/dom_tests.coffee create mode 100644 tests/dom_tests/dom_tests.html create mode 100644 tests/dom_tests/phantom_runner.coffee delete mode 100644 tests/test_helper.coffee create mode 100644 tests/unit_tests/completion_test.coffee create mode 100644 tests/unit_tests/test_helper.coffee create mode 100644 tests/unit_tests/utils_test.coffee delete mode 100644 tests/utils_test.coffee diff --git a/Cakefile b/Cakefile index 697c03dc..1c324497 100644 --- a/Cakefile +++ b/Cakefile @@ -55,10 +55,20 @@ task "package", "build .crx file", -> crxmake.stdout.on "data", (data) -> console.log data.toString().trim() crxmake.on "exit", -> fs.writeFileSync "manifest.json", orig_manifest_text -task "test", "run all unit tests", -> - test_files = fs.readdirSync("tests/").filter((filename) -> filename.indexOf("_test.js") > 0) - test_files = test_files.map((filename) -> "tests/" + filename) +task "test", "run all tests", -> + console.log "Running unit tests..." + basedir = "tests/unit_tests/" + test_files = fs.readdirSync(basedir).filter((filename) -> filename.indexOf("_test.js") > 0) + test_files = test_files.map((filename) -> basedir + filename) test_files.forEach (file) -> require "./" + file Tests.run() - if Tests.testsFailed > 0 - process.exit 1 + returnCode = if Tests.testsFailed > 0 then 1 else 0 + + console.log "Running DOM tests..." + spawn = (require "child_process").spawn + phantom = spawn "phantomjs", ["./tests/dom_tests/phantom_runner.js"] + phantom.stdout.on 'data', (data) -> process.stdout.write data + phantom.stderr.on 'data', (data) -> process.stderr.write data + phantom.on 'exit', (code) -> + returnCode += code + process.exit returnCode diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 4a68ced6..bc3fde0b 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -553,7 +553,7 @@ sendRequestHandlers = refreshCompleter: refreshCompleter # Convenience function for development use. -window.runTests = -> open(chrome.extension.getURL('test_harnesses/automated/automated.html')) +window.runTests = -> open(chrome.extension.getURL('tests/dom_tests/dom_tests.html')) # # Begin initialization. diff --git a/test_harnesses/automated/automated.coffee b/test_harnesses/automated/automated.coffee deleted file mode 100644 index b5e5af35..00000000 --- a/test_harnesses/automated/automated.coffee +++ /dev/null @@ -1,197 +0,0 @@ -# -# 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" + "trackalt text" - 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 = "alt text" + "alt text" + "" + "" - 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 = "" + "" + "" + "" + "" - 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 += "
" + newOutput + "
" - console.log.apply console, args - -# ensure the extension has time to load before commencing the tests -document.addEventListener "DOMContentLoaded", -> - setTimeout Tests.run, 200 - -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 deleted file mode 100644 index a0c8c7de..00000000 --- a/test_harnesses/automated/automated.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - -
- -

Vimium Tests

- -
- - - diff --git a/tests/completion_test.coffee b/tests/completion_test.coffee deleted file mode 100644 index 7094d720..00000000 --- a/tests/completion_test.coffee +++ /dev/null @@ -1,165 +0,0 @@ -require "./test_helper.js" -extend(global, require "../lib/utils.js") -extend(global, require "../background_scripts/completion.js") - -global.chrome = {} - -context "bookmark completer", - setup -> - @bookmark2 = { title: "bookmark2", url: "bookmark2.com" } - @bookmark1 = { title: "bookmark1", url: "bookmark1.com", children: [@bookmark2] } - global.chrome.bookmarks = - getTree: (callback) => callback([@bookmark1]) - - @completer = new BookmarkCompleter() - - should "flatten a list of bookmarks", -> - result = @completer.traverseBookmarks([@bookmark1]) - assert.arrayEqual [@bookmark1, @bookmark2], @completer.traverseBookmarks([@bookmark1]) - - should "return matching bookmarks when searching", -> - @completer.refresh() - results = filterCompleter(@completer, ["mark2"]) - assert.arrayEqual [@bookmark2.url], results.map (suggestion) -> suggestion.url - -context "HistoryCache", - context "binary search", - setup -> - @compare = (a, b) -> a - b - - should "find elements to the left of the middle", -> - assert.equal 0, HistoryCache.binarySearch(3, [3, 5, 8], @compare) - - should "find elements to the right of the middle", -> - assert.equal 2, HistoryCache.binarySearch(8, [3, 5, 8], @compare) - - context "unfound elements", - should "return 0 if it should be the head of the list", -> - assert.equal 0, HistoryCache.binarySearch(1, [3, 5, 8], @compare) - - should "return length - 1 if it should be at the end of the list", -> - assert.equal 0, HistoryCache.binarySearch(3, [3, 5, 8], @compare) - - should "found return the position if it's between two elements", -> - assert.equal 1, HistoryCache.binarySearch(4, [3, 5, 8], @compare) - assert.equal 2, HistoryCache.binarySearch(7, [3, 5, 8], @compare) - - context "fetchHistory", - setup -> - @history1 = { url: "b.com", lastVisitTime: 5 } - @history2 = { url: "a.com", lastVisitTime: 10 } - history = [@history1, @history2] - @onVisitedListener = null - global.chrome.history = - search: (options, callback) -> callback(history) - onVisited: { addListener: (@onVisitedListener) => } - HistoryCache.reset() - - should "store visits sorted by url ascending", -> - HistoryCache.use (@results) => - assert.arrayEqual [@history2, @history1], @results - - should "add new visits to the history", -> - HistoryCache.use () -> - newSite = { url: "ab.com" } - @onVisitedListener(newSite) - HistoryCache.use (@results) => - assert.arrayEqual [@history2, newSite, @history1], @results - - should "replace new visits in the history", -> - HistoryCache.use (@results) => - assert.arrayEqual [@history2, @history1], @results - newSite = { url: "a.com", lastVisitTime: 15 } - @onVisitedListener(newSite) - HistoryCache.use (@results) => - assert.arrayEqual [newSite, @history1], @results - -context "history completer", - setup -> - @history1 = { title: "history1", url: "history1.com", lastVisitTime: hours(1) } - @history2 = { title: "history2", url: "history2.com", lastVisitTime: hours(5) } - - global.chrome.history = - search: (options, callback) => callback([@history1, @history2]) - onVisited: { addListener: -> } - - @completer = new HistoryCompleter() - - should "return matching history entries when searching", -> - assert.arrayEqual [@history1.url], filterCompleter(@completer, ["story1"]).map (entry) -> entry.url - - should "rank recent results higher than nonrecent results", -> - stub(Date, "now", returns(hours(24))) - results = filterCompleter(@completer, ["hist"]) - results.forEach (result) -> result.computeRelevancy() - results.sort (a, b) -> b.relevancy - a.relevancy - assert.arrayEqual [@history2.url, @history1.url], results.map (result) -> result.url - -context "domain completer", - setup -> - @history1 = { title: "history1", url: "http://history1.com", lastVisitTime: hours(1) } - @history2 = { title: "history2", url: "http://history2.com", lastVisitTime: hours(1) } - - stub(HistoryCache, "use", (onComplete) => onComplete([@history1, @history2])) - global.chrome.history = { onVisited: { addListener: -> } } - stub(Date, "now", returns(hours(24))) - - @completer = new DomainCompleter() - - should "return only a single matching domain", -> - results = filterCompleter(@completer, ["story"]) - assert.arrayEqual ["history1.com"], results.map (result) -> result.url - - should "pick domains which are more recent", -> - # This domains are the same except for their last visited time. - assert.equal "history1.com", filterCompleter(@completer, ["story"])[0].url - @history2.lastVisitTime = hours(3) - assert.equal "history2.com", filterCompleter(@completer, ["story"])[0].url - - should "returns no results when there's more than one query term, because clearly it's not a domain", -> - assert.arrayEqual [], filterCompleter(@completer, ["his", "tory"]) - -context "tab completer", - setup -> - @tabs = [ - { url: "tab1.com", title: "tab1", id: 1 } - { url: "tab2.com", title: "tab2", id: 2 }] - chrome.tabs = { query: (args, onComplete) => onComplete(@tabs) } - @completer = new TabCompleter() - - should "return matching tabs", -> - results = filterCompleter(@completer, ["tab2"]) - assert.equal "tab2.com", results.map (tab) -> tab.url - assert.equal 2, results.map (tab) -> tab.tabId - -context "suggestions", - should "escape html in page titles", -> - suggestion = new Suggestion(["queryterm"], "tab", "url", "title ", returns(1)) - assert.isTrue suggestion.generateHtml().indexOf("title <span>") >= 0 - - should "highlight query words", -> - suggestion = new Suggestion(["ninj", "words"], "tab", "url", "ninjawords", returns(1)) - expected = "ninjawords" - assert.isTrue suggestion.generateHtml().indexOf(expected) >= 0 - - should "highlight query words correctly when whey they overlap", -> - suggestion = new Suggestion(["ninj", "jaword"], "tab", "url", "ninjawords", returns(1)) - expected = "ninjawords" - assert.isTrue suggestion.generateHtml().indexOf(expected) >= 0 - - should "shorten urls", -> - suggestion = new Suggestion(["queryterm"], "tab", "http://ninjawords.com", "ninjawords", returns(1)) - assert.equal -1, suggestion.generateHtml().indexOf("http://ninjawords.com") - -context "RankingUtils", - should "do a case insensitive match", -> - assert.isTrue RankingUtils.matches(["maRiO"], "MARIO", "MARIo") - - -# A convenience wrapper around completer.filter() so it can be called synchronously in tests. -filterCompleter = (completer, queryTerms) -> - results = [] - completer.filter(queryTerms, (completionResults) -> results = completionResults) - results - -hours = (n) -> 1000 * 60 * 60 * n diff --git a/tests/dom_tests/bind.js b/tests/dom_tests/bind.js new file mode 100644 index 00000000..833f8006 --- /dev/null +++ b/tests/dom_tests/bind.js @@ -0,0 +1,27 @@ +/* + * Polyfill taken from https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind + * Necessary because the current version of PhantomJS doesn't yet support bind(). + */ +if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== "function") { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () {}, + fBound = function () { + return fToBind.apply(this instanceof fNOP && oThis + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + }; +} diff --git a/tests/dom_tests/chrome.coffee b/tests/dom_tests/chrome.coffee new file mode 100644 index 00000000..ff7a53d0 --- /dev/null +++ b/tests/dom_tests/chrome.coffee @@ -0,0 +1,20 @@ +# +# Mock the Chrome extension API. +# + +root = exports ? window + +root.chrome = { + extension: { + connect: -> { + onMessage: { + addListener: -> + } + postMessage: -> + } + onRequest: { + addListener: -> + } + sendRequest: -> + } +} diff --git a/tests/dom_tests/dom_tests.coffee b/tests/dom_tests/dom_tests.coffee new file mode 100644 index 00000000..f4e63270 --- /dev/null +++ b/tests/dom_tests/dom_tests.coffee @@ -0,0 +1,200 @@ +# +# 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 + stub settings.values, "linkHintCharacters", "ab" + + 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" + "trackalt text" + 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 = "alt text" + "alt text" + "" + "" + 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 = "" + "" + "" + "" + "" + 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 += "
" + newOutput + "
" + console.log.apply console, args + +# PhantomJS will call the tests manually +unless navigator.userAgent == 'phantom' + # ensure the extension has time to load before commencing the tests + document.addEventListener "DOMContentLoaded", -> + setTimeout Tests.run, 200 + +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/tests/dom_tests/dom_tests.html b/tests/dom_tests/dom_tests.html new file mode 100644 index 00000000..1dc45782 --- /dev/null +++ b/tests/dom_tests/dom_tests.html @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + +
+ +

Vimium Tests

+ +
+ + + diff --git a/tests/dom_tests/phantom_runner.coffee b/tests/dom_tests/phantom_runner.coffee new file mode 100644 index 00000000..f0f2b128 --- /dev/null +++ b/tests/dom_tests/phantom_runner.coffee @@ -0,0 +1,32 @@ +page = require('webpage').create() + +page.settings.userAgent = 'phantom' + +# ensure that the elements we test the link hints on are actually visible +page.viewportSize = + width: 900 + height: 600 + +page.onConsoleMessage = (msg) -> + console.log msg + +system = require 'system' +fs = require 'fs' + +pathParts = system.args[0].split(fs.separator) +pathParts[pathParts.length - 1] = '' +dirname = pathParts.join(fs.separator) + +page.open dirname + 'dom_tests.html', (status) -> + if status != 'success' + console.log 'Unable to load tests.' + phantom.exit 1 + + testsFailed = page.evaluate -> + Tests.run() + return Tests.testsFailed + + if testsFailed > 0 + phantom.exit 1 + else + phantom.exit 0 diff --git a/tests/test_helper.coffee b/tests/test_helper.coffee deleted file mode 100644 index 237f8e24..00000000 --- a/tests/test_helper.coffee +++ /dev/null @@ -1,5 +0,0 @@ -require("./shoulda.js/shoulda.js") -global.extend = (hash1, hash2) -> - for key of hash2 - hash1[key] = hash2[key] - hash1 \ No newline at end of file diff --git a/tests/unit_tests/completion_test.coffee b/tests/unit_tests/completion_test.coffee new file mode 100644 index 00000000..d3369398 --- /dev/null +++ b/tests/unit_tests/completion_test.coffee @@ -0,0 +1,165 @@ +require "./test_helper.js" +extend(global, require "../../lib/utils.js") +extend(global, require "../../background_scripts/completion.js") + +global.chrome = {} + +context "bookmark completer", + setup -> + @bookmark2 = { title: "bookmark2", url: "bookmark2.com" } + @bookmark1 = { title: "bookmark1", url: "bookmark1.com", children: [@bookmark2] } + global.chrome.bookmarks = + getTree: (callback) => callback([@bookmark1]) + + @completer = new BookmarkCompleter() + + should "flatten a list of bookmarks", -> + result = @completer.traverseBookmarks([@bookmark1]) + assert.arrayEqual [@bookmark1, @bookmark2], @completer.traverseBookmarks([@bookmark1]) + + should "return matching bookmarks when searching", -> + @completer.refresh() + results = filterCompleter(@completer, ["mark2"]) + assert.arrayEqual [@bookmark2.url], results.map (suggestion) -> suggestion.url + +context "HistoryCache", + context "binary search", + setup -> + @compare = (a, b) -> a - b + + should "find elements to the left of the middle", -> + assert.equal 0, HistoryCache.binarySearch(3, [3, 5, 8], @compare) + + should "find elements to the right of the middle", -> + assert.equal 2, HistoryCache.binarySearch(8, [3, 5, 8], @compare) + + context "unfound elements", + should "return 0 if it should be the head of the list", -> + assert.equal 0, HistoryCache.binarySearch(1, [3, 5, 8], @compare) + + should "return length - 1 if it should be at the end of the list", -> + assert.equal 0, HistoryCache.binarySearch(3, [3, 5, 8], @compare) + + should "found return the position if it's between two elements", -> + assert.equal 1, HistoryCache.binarySearch(4, [3, 5, 8], @compare) + assert.equal 2, HistoryCache.binarySearch(7, [3, 5, 8], @compare) + + context "fetchHistory", + setup -> + @history1 = { url: "b.com", lastVisitTime: 5 } + @history2 = { url: "a.com", lastVisitTime: 10 } + history = [@history1, @history2] + @onVisitedListener = null + global.chrome.history = + search: (options, callback) -> callback(history) + onVisited: { addListener: (@onVisitedListener) => } + HistoryCache.reset() + + should "store visits sorted by url ascending", -> + HistoryCache.use (@results) => + assert.arrayEqual [@history2, @history1], @results + + should "add new visits to the history", -> + HistoryCache.use () -> + newSite = { url: "ab.com" } + @onVisitedListener(newSite) + HistoryCache.use (@results) => + assert.arrayEqual [@history2, newSite, @history1], @results + + should "replace new visits in the history", -> + HistoryCache.use (@results) => + assert.arrayEqual [@history2, @history1], @results + newSite = { url: "a.com", lastVisitTime: 15 } + @onVisitedListener(newSite) + HistoryCache.use (@results) => + assert.arrayEqual [newSite, @history1], @results + +context "history completer", + setup -> + @history1 = { title: "history1", url: "history1.com", lastVisitTime: hours(1) } + @history2 = { title: "history2", url: "history2.com", lastVisitTime: hours(5) } + + global.chrome.history = + search: (options, callback) => callback([@history1, @history2]) + onVisited: { addListener: -> } + + @completer = new HistoryCompleter() + + should "return matching history entries when searching", -> + assert.arrayEqual [@history1.url], filterCompleter(@completer, ["story1"]).map (entry) -> entry.url + + should "rank recent results higher than nonrecent results", -> + stub(Date, "now", returns(hours(24))) + results = filterCompleter(@completer, ["hist"]) + results.forEach (result) -> result.computeRelevancy() + results.sort (a, b) -> b.relevancy - a.relevancy + assert.arrayEqual [@history2.url, @history1.url], results.map (result) -> result.url + +context "domain completer", + setup -> + @history1 = { title: "history1", url: "http://history1.com", lastVisitTime: hours(1) } + @history2 = { title: "history2", url: "http://history2.com", lastVisitTime: hours(1) } + + stub(HistoryCache, "use", (onComplete) => onComplete([@history1, @history2])) + global.chrome.history = { onVisited: { addListener: -> } } + stub(Date, "now", returns(hours(24))) + + @completer = new DomainCompleter() + + should "return only a single matching domain", -> + results = filterCompleter(@completer, ["story"]) + assert.arrayEqual ["history1.com"], results.map (result) -> result.url + + should "pick domains which are more recent", -> + # This domains are the same except for their last visited time. + assert.equal "history1.com", filterCompleter(@completer, ["story"])[0].url + @history2.lastVisitTime = hours(3) + assert.equal "history2.com", filterCompleter(@completer, ["story"])[0].url + + should "returns no results when there's more than one query term, because clearly it's not a domain", -> + assert.arrayEqual [], filterCompleter(@completer, ["his", "tory"]) + +context "tab completer", + setup -> + @tabs = [ + { url: "tab1.com", title: "tab1", id: 1 } + { url: "tab2.com", title: "tab2", id: 2 }] + chrome.tabs = { query: (args, onComplete) => onComplete(@tabs) } + @completer = new TabCompleter() + + should "return matching tabs", -> + results = filterCompleter(@completer, ["tab2"]) + assert.equal "tab2.com", results.map (tab) -> tab.url + assert.equal 2, results.map (tab) -> tab.tabId + +context "suggestions", + should "escape html in page titles", -> + suggestion = new Suggestion(["queryterm"], "tab", "url", "title ", returns(1)) + assert.isTrue suggestion.generateHtml().indexOf("title <span>") >= 0 + + should "highlight query words", -> + suggestion = new Suggestion(["ninj", "words"], "tab", "url", "ninjawords", returns(1)) + expected = "ninjawords" + assert.isTrue suggestion.generateHtml().indexOf(expected) >= 0 + + should "highlight query words correctly when whey they overlap", -> + suggestion = new Suggestion(["ninj", "jaword"], "tab", "url", "ninjawords", returns(1)) + expected = "ninjawords" + assert.isTrue suggestion.generateHtml().indexOf(expected) >= 0 + + should "shorten urls", -> + suggestion = new Suggestion(["queryterm"], "tab", "http://ninjawords.com", "ninjawords", returns(1)) + assert.equal -1, suggestion.generateHtml().indexOf("http://ninjawords.com") + +context "RankingUtils", + should "do a case insensitive match", -> + assert.isTrue RankingUtils.matches(["maRiO"], "MARIO", "MARIo") + + +# A convenience wrapper around completer.filter() so it can be called synchronously in tests. +filterCompleter = (completer, queryTerms) -> + results = [] + completer.filter(queryTerms, (completionResults) -> results = completionResults) + results + +hours = (n) -> 1000 * 60 * 60 * n diff --git a/tests/unit_tests/test_helper.coffee b/tests/unit_tests/test_helper.coffee new file mode 100644 index 00000000..bb73bf54 --- /dev/null +++ b/tests/unit_tests/test_helper.coffee @@ -0,0 +1,5 @@ +require("../shoulda.js/shoulda.js") +global.extend = (hash1, hash2) -> + for key of hash2 + hash1[key] = hash2[key] + hash1 diff --git a/tests/unit_tests/utils_test.coffee b/tests/unit_tests/utils_test.coffee new file mode 100644 index 00000000..6a44b460 --- /dev/null +++ b/tests/unit_tests/utils_test.coffee @@ -0,0 +1,20 @@ +require "./test_helper.js" +extend(global, require "../../lib/utils.js") + +context "convertToUrl", + should "detect and clean up valid URLs", -> + assert.equal "http://www.google.com/", Utils.convertToUrl("http://www.google.com/") + assert.equal "http://www.google.com/", Utils.convertToUrl(" http://www.google.com/ ") + assert.equal "http://www.google.com", Utils.convertToUrl("www.google.com") + assert.equal "http://google.com", Utils.convertToUrl("google.com") + assert.equal "http://localhost", Utils.convertToUrl("localhost") + assert.equal "http://xyz.museum", Utils.convertToUrl("xyz.museum") + assert.equal "chrome://extensions", Utils.convertToUrl("chrome://extensions") + assert.equal "http://user:pass@ftp.xyz.com/test", Utils.convertToUrl("user:pass@ftp.xyz.com/test") + assert.equal "http://127.0.0.1", Utils.convertToUrl("127.0.0.1") + assert.equal "http://127.0.0.1:8080", Utils.convertToUrl("127.0.0.1:8080") + assert.equal "http://[::]:8080", Utils.convertToUrl("[::]:8080") + + should "convert non-URL terms into search queries", -> + assert.equal "http://www.google.com/search?q=google", Utils.convertToUrl("google") + assert.equal "http://www.google.com/search?q=go%20ogle.com", Utils.convertToUrl("go ogle.com") diff --git a/tests/utils_test.coffee b/tests/utils_test.coffee deleted file mode 100644 index c5e5d002..00000000 --- a/tests/utils_test.coffee +++ /dev/null @@ -1,20 +0,0 @@ -require "./test_helper.js" -extend(global, require "../lib/utils.js") - -context "convertToUrl", - should "detect and clean up valid URLs", -> - assert.equal "http://www.google.com/", Utils.convertToUrl("http://www.google.com/") - assert.equal "http://www.google.com/", Utils.convertToUrl(" http://www.google.com/ ") - assert.equal "http://www.google.com", Utils.convertToUrl("www.google.com") - assert.equal "http://google.com", Utils.convertToUrl("google.com") - assert.equal "http://localhost", Utils.convertToUrl("localhost") - assert.equal "http://xyz.museum", Utils.convertToUrl("xyz.museum") - assert.equal "chrome://extensions", Utils.convertToUrl("chrome://extensions") - assert.equal "http://user:pass@ftp.xyz.com/test", Utils.convertToUrl("user:pass@ftp.xyz.com/test") - assert.equal "http://127.0.0.1", Utils.convertToUrl("127.0.0.1") - assert.equal "http://127.0.0.1:8080", Utils.convertToUrl("127.0.0.1:8080") - assert.equal "http://[::]:8080", Utils.convertToUrl("[::]:8080") - - should "convert non-URL terms into search queries", -> - assert.equal "http://www.google.com/search?q=google", Utils.convertToUrl("google") - assert.equal "http://www.google.com/search?q=go%20ogle.com", Utils.convertToUrl("go ogle.com") -- cgit v1.2.3