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" + "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 += "
" + 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" + "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 += "" + 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