Fix bugs and add tests (#145)

* Fix bug when checking if elements were parsed already

parse-element.js checks if the element was already parsed by
checking for the `data-pjax-click-state` attribute. However, this
attribute was not added until the link is clicked.

Originally, there was a separate attribute, `data-pjax-enabled`,
which tracked if the element was parsed already, but that was
changed in 9a86044.

This commit merges the attributes for mouse clicks and key presses
into one and adds that attribute when the element is initially
parsed.

* More bug fixes

* Fix documentation for currentUrlFullReload

* Ignore lines from coverage if they can't be tested

* Refactor attach-link and attach-form

* Fix and refactors tests

* Add tests

* Add TS definitions for options.requestOptions

* Code cleanup
This commit was merged in pull request #145.
This commit is contained in:
BehindTheMath
2018-04-09 23:36:32 -04:00
committed by GitHub
parent 17d8262025
commit d6bf21ed22
22 changed files with 478 additions and 153 deletions

View File

@@ -14,8 +14,8 @@ tape("test evalScript method", function(t) {
t.equal(document.body.className, "executed", "script has been properly executed")
script.innerHTML = "document.write('failure')"
document.body.text = "document.write hasn't been executed"
var bodyText = document.body.text
var bodyText = "document.write hasn't been executed"
document.body.text = bodyText
evalScript(script)
t.equal(document.body.text, bodyText, "document.write hasn't been executed")

View File

@@ -2,7 +2,7 @@ var tape = require("tape")
var executeScripts = require("../../lib/execute-scripts")
tape("test executeScripts method", function(t) {
tape("test executeScripts method when the script tag is inside a container", function(t) {
document.body.className = ""
var container = document.createElement("div")
@@ -14,3 +14,16 @@ tape("test executeScripts method", function(t) {
t.end()
})
tape("test executeScripts method with just a script tag", function(t) {
document.body.className = ""
var script = document.createElement("script")
script.innerHTML = "document.body.className = 'executed correctly';"
t.equal(document.body.className, "", "script hasn't been executed yet")
executeScripts(script)
t.equal(document.body.className, "executed correctly", "script has been properly executed")
t.end()
})

View File

@@ -4,20 +4,18 @@ var on = require("../../../lib/events/on")
var trigger = require("../../../lib/events/trigger")
var attachForm = require("../../../lib/proto/attach-form")
var form = document.createElement("form")
var attr = "data-pjax-click-state"
var preventDefault = function(e) { e.preventDefault() }
var attr = "data-pjax-state"
tape("test attach form prototype method", function(t) {
t.plan(7)
var form = document.createElement("form")
var loadUrlCalled = false
attachForm.call({
options: {},
reload: function() {
t.equal(form.getAttribute(attr), "reload", "triggering a simple reload will just submit the form")
options: {
currentUrlFullReload: true
},
loadUrl: function() {
t.equal(form.getAttribute(attr), "submit", "triggering a post to the next page")
loadUrlCalled = true
}
}, form)
@@ -29,50 +27,57 @@ tape("test attach form prototype method", function(t) {
form.action = internalUri + "#anchor"
trigger(form, "submit")
t.equal(form.getAttribute(attr), "anchor-present", "internal anchor stop behavior")
t.equal(form.getAttribute(attr), "anchor", "internal anchor stop behavior")
window.location.hash = "#anchor"
form.action = internalUri + "#another-anchor"
trigger(form, "submit")
t.notEqual(form.getAttribute(attr), "anchor", "differents anchors stop behavior")
t.equal(form.getAttribute(attr), "anchor", "different anchors stop behavior")
window.location.hash = ""
form.action = internalUri + "#"
trigger(form, "submit")
t.equal(form.getAttribute(attr), "anchor-empty", "empty anchor stop behavior")
form.action = internalUri
form.action = window.location.href
trigger(form, "submit")
// see reload defined above
t.equal(form.getAttribute(attr), "reload", "submitting when currentUrlFullReload is true will submit normally, without XHR")
t.equal(loadUrlCalled, false, "loadUrl() not called")
form.action = window.location.protocol + "//" + window.location.host + "/internal"
form.method = "POST"
trigger(form, "submit")
// see post defined above
t.equal(form.getAttribute(attr), "submit", "triggering a POST request to the next page")
t.equal(loadUrlCalled, true, "loadUrl() called correctly")
loadUrlCalled = false
form.setAttribute(attr, "")
form.action = window.location.protocol + "//" + window.location.host + "/internal"
form.method = "GET"
trigger(form, "submit")
// see post defined above
t.equal(form.getAttribute(attr), "submit", "triggering a GET request to the next page")
t.equal(loadUrlCalled, true, "loadUrl() called correctly")
t.end()
})
tape("test attach form preventDefaulted events", function(t) {
var callbacked = false
var loadUrlCalled = false
var form = document.createElement("form")
// This needs to be before the call to attachForm()
on(form, "submit", function(event) { event.preventDefault() })
attachForm.call({
options: {},
loadUrl: function() {
callbacked = true
loadUrlCalled = true
}
}, form)
form.action = "#"
on(form, "submit", preventDefault)
trigger(form, "submit")
t.equal(callbacked, false, "events that are preventDefaulted should not fire callback")
t.equal(loadUrlCalled, false, "events that are preventDefaulted should not fire callback")
t.end()
})
@@ -93,3 +98,57 @@ tape("test options are not modified by attachForm", function(t) {
t.end()
})
tape("test submit triggered by keyboard", function(t) {
var form = document.createElement("form")
var pjax = {
options: {},
loadUrl: function() {
t.equal(form.getAttribute(attr), "submit", "triggering a internal link actually submits the form")
}
}
t.plan(2)
attachForm.call(pjax, form)
form.action = window.location.protocol + "//" + window.location.host + "/internal"
trigger(form, "keyup", {keyCode: 14})
t.equal(form.getAttribute(attr), "", "keycode other than 13 doesn't trigger anything")
trigger(form, "keyup", {keyCode: 13})
// see loadUrl defined above
t.end()
})
tape("test form elements parsed correctly", function(t) {
t.plan(1)
var form = document.createElement("form")
var input = document.createElement("input")
input.name = "input"
input.value = "value"
form.appendChild(input)
var params = [{
name: "input",
value: "value"
}]
var pjax = {
options: {},
loadUrl: function(href, options) {
t.same(options.requestOptions.requestParams, params, "form elements parsed correctly")
}
}
attachForm.call(pjax, form)
form.action = window.location.protocol + "//" + window.location.host + "/internal"
trigger(form, "submit")
// see loadUrl defined above
t.end()
})

View File

@@ -4,27 +4,22 @@ var on = require("../../../lib/events/on")
var trigger = require("../../../lib/events/trigger")
var attachLink = require("../../../lib/proto/attach-link")
var a = document.createElement("a")
var attr = "data-pjax-click-state"
var preventDefault = function(e) { e.preventDefault() }
var attr = "data-pjax-state"
tape("test attach link prototype method", function(t) {
t.plan(7)
var a = document.createElement("a")
var loadUrlCalled = false
attachLink.call({
options: {},
reload: function() {
t.equal(a.getAttribute(attr), "reload", "triggering exact same url reload the page")
},
loadUrl: function() {
t.equal(a.getAttribute(attr), "load", "triggering a internal link actually load the page")
loadUrlCalled = true
}
}, a)
var internalUri = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.search
a.href = internalUri
on(a, "click", preventDefault) // to avoid link to be open (break testing env)
trigger(a, "click", {metaKey: true})
t.equal(a.getAttribute(attr), "modifier", "event key modifier stop behavior")
@@ -32,46 +27,47 @@ tape("test attach link prototype method", function(t) {
trigger(a, "click")
t.equal(a.getAttribute(attr), "external", "external url stop behavior")
window.location.hash = "#anchor"
a.href = internalUri + "#anchor"
trigger(a, "click")
t.equal(a.getAttribute(attr), "anchor-present", "internal anchor stop behavior")
t.equal(a.getAttribute(attr), "anchor", "internal anchor stop behavior")
window.location.hash = "#anchor"
a.href = internalUri + "#another-anchor"
trigger(a, "click")
t.notEqual(a.getAttribute(attr), "anchor", "differents anchors stop behavior")
t.equal(a.getAttribute(attr), "anchor", "different anchors stop behavior")
window.location.hash = ""
a.href = internalUri + "#"
trigger(a, "click")
t.equal(a.getAttribute(attr), "anchor-empty", "empty anchor stop behavior")
a.href = internalUri
trigger(a, "click")
// see reload defined above
a.href = window.location.protocol + "//" + window.location.host + "/internal"
trigger(a, "click")
// see loadUrl defined above
t.equals(a.getAttribute(attr), "load", "triggering an internal link sets the state attribute to 'load'")
t.equals(loadUrlCalled, true, "triggering an internal link actually loads the page")
t.end()
})
tape("test attach link preventDefaulted events", function(t) {
var callbacked = false
var loadUrlCalled = false
var a = document.createElement("a")
// This needs to be before the call to attachLink()
on(a, "click", function(event) {
event.preventDefault()
})
attachLink.call({
options: {},
loadUrl: function() {
callbacked = true
loadUrlCalled = true
}
}, a)
a.href = "#"
on(a, "click", preventDefault)
trigger(a, "click")
t.equal(callbacked, false, "events that are preventDefaulted should not fire callback")
t.equal(loadUrlCalled, false, "events that are preventDefaulted should not fire callback")
t.end()
})
@@ -92,3 +88,56 @@ tape("test options are not modified by attachLink", function(t) {
t.end()
})
tape("test link triggered by keyboard", function(t) {
var a = document.createElement("a")
var pjax = {
options: {},
loadUrl: function() {
t.equal(a.getAttribute(attr), "load", "triggering a internal link actually loads the page")
}
}
t.plan(3)
attachLink.call(pjax, a)
a.href = window.location.protocol + "//" + window.location.host + "/internal"
trigger(a, "keyup", {keyCode: 14})
t.equal(a.getAttribute(attr), "", "keycode other than 13 doesn't trigger anything")
trigger(a, "keyup", {keyCode: 13, metaKey: true})
t.equal(a.getAttribute(attr), "modifier", "event key modifier stop behavior")
trigger(a, "keyup", {keyCode: 13})
// see loadUrl defined above
t.end()
})
tape("test link with the same URL as the current one, when currentUrlFullReload set to true", function(t) {
var a = document.createElement("a")
var pjax = {
options: {
currentUrlFullReload: true
},
reload: function() {
t.pass("this.reload() was called correctly")
},
loadUrl: function() {
t.fail("loadUrl() was called wrongly")
}
}
t.plan(2)
attachLink.call(pjax, a)
a.href = window.location.href
trigger(a, "click")
t.equal(a.getAttribute(attr), "reload", "reload stop behavior")
t.end()
})

View File

@@ -1,7 +1,8 @@
var tape = require("tape")
var parseElement = require("../../../lib/proto/parse-element")
var protoMock = {
var pjax = {
attachLink: function() { return true },
attachForm: function() { return true }
}
@@ -9,13 +10,18 @@ var protoMock = {
tape("test parse element prototype method", function(t) {
t.doesNotThrow(function() {
var a = document.createElement("a")
parseElement.call(protoMock, a)
parseElement.call(pjax, a)
}, "<a> element can be parsed")
t.doesNotThrow(function() {
var form = document.createElement("form")
parseElement.call(protoMock, form)
parseElement.call(pjax, form)
}, "<form> element can be parsed")
t.throws(function() {
var el = document.createElement("div")
parseElement.call(pjax, el)
}, "<div> element cannot be parsed")
t.end()
})

View File

@@ -57,3 +57,81 @@ tape("request headers are sent properly", function(t) {
})
})
tape("HTTP status codes other than 200 are handled properly", function(t) {
var url = "https://httpbin.org/status/400"
sendRequest(url, {}, function(responseText, request) {
t.equals(responseText, null, "responseText is null")
t.equals(request.status, 400, "HTTP status code is correct")
t.end()
})
})
tape.skip("XHR error is handled properly", function(t) {
var url = "https://encrypted.google.com/foobar"
sendRequest(url, {}, function(responseText) {
t.equals(responseText, null, "responseText is null")
t.end()
})
})
tape("POST body data is sent properly", function(t) {
var url = "https://httpbin.org/post"
var params = [{
name: "test",
value: "1"
}];
var options = {
requestOptions: {
requestMethod: "POST",
requestParams: params
}
}
sendRequest(url, options, function(responseText) {
var response = JSON.parse(responseText)
t.same(response.form[params[0].name], params[0].value, "requestParams were sent properly")
t.equals(response.headers["Content-Type"], "application/x-www-form-urlencoded", "Content-Type header was set properly")
t.end()
})
})
tape("GET query data is sent properly", function(t) {
var url = "https://httpbin.org/get"
var params = [{
name: "test",
value: "1"
}];
var options = {
requestOptions: {
requestParams: params
}
}
sendRequest(url, options, function(responseText) {
var response = JSON.parse(responseText)
t.same(response.args[params[0].name], params[0].value, "requestParams were sent properly")
t.end()
})
})
tape("XHR timeout is handled properly", function(t) {
var url = "https://httpbin.org/delay/5"
var options = {
timeout: 1000
}
sendRequest(url, options, function(responseText) {
t.equals(responseText, null, "responseText is null")
t.end()
})
})

View File

@@ -1,18 +1,20 @@
var tape = require("tape")
var switchesSelectors = require("../../lib/switches-selectors.js")
var noop = require("../../lib/util/noop")
var pjax = {
onSwitch: function() {
console.log("Switched")
},
state: {},
log: noop
}
// @author darylteo
tape("test switchesSelectors", function(t) {
// switchesSelectors relies on a higher level function callback
// should really be passed in instead so I'll leave it here as a TODO:
var pjax = {
onSwitch: function() {
console.log("Switched")
},
state: {}
}
var tmpEl = document.implementation.createHTMLDocument()
// a div container is used because swapping the containers
@@ -40,3 +42,33 @@ tape("test switchesSelectors", function(t) {
t.end()
})
tape("test switchesSelectors when number of elements don't match", function(t) {
var newTempDoc = document.implementation.createHTMLDocument()
var originalTempDoc = document.implementation.createHTMLDocument()
// a div container is used because swapping the containers
// will generate a new element, so things get weird
// using "body" generates a lot of testling cruft that I don't
// want so let's avoid that
var container = originalTempDoc.createElement("div")
container.innerHTML = "<p>Original text</p><span>No change</span>"
originalTempDoc.body.appendChild(container)
var container2 = newTempDoc.createElement("div")
container2.innerHTML = "<p>New text</p><p>More new text</p><span>New span</span>"
newTempDoc.body.appendChild(container2)
var switchSelectorsFn = switchesSelectors.bind(pjax,
{}, // switches
{}, // switchesOptions
["p"], // selectors,
newTempDoc, // fromEl
originalTempDoc, // toEl,
{} // options
)
t.throws(switchSelectorsFn, null, "error was thrown properly since number of elements don't match")
t.end()
})

View File

@@ -2,6 +2,60 @@ var tape = require("tape")
var switches = require("../../lib/switches")
var noop = require("../../lib/util/noop")
tape("test outerHTML switch", function(t) {
var outerHTML = switches.outerHTML
var doc = document.implementation.createHTMLDocument()
var container = doc.createElement("div")
container.innerHTML = "<p id='p'>Original Text</p>"
doc.body.appendChild(container)
var p = doc.createElement("p")
p.innerHTML = "New Text"
outerHTML.bind({
onSwitch: noop
})(doc.querySelector("p"), p)
t.equals(doc.querySelector("p").innerHTML, "New Text", "Elements correctly switched")
t.notEquals(doc.querySelector("p").id, "p", "other attributes overwritten correctly")
t.end()
})
tape("test innerHTML switch", function(t) {
var innerHTML = switches.innerHTML
var doc = document.implementation.createHTMLDocument()
var container = doc.createElement("div")
container.innerHTML = "<p id='p'>Original Text</p>"
doc.body.appendChild(container)
var p = doc.createElement("p")
p.innerHTML = "New Text"
p.className = "p"
innerHTML.bind({
onSwitch: noop
})(doc.querySelector("p"), p)
t.equals(doc.querySelector("p").innerHTML, "New Text", "Elements correctly switched")
t.equals(doc.querySelector("p").className, "p", "classname set correctly")
t.equals(doc.querySelector("p").id, "p", "other attributes set correctly")
p.removeAttribute("class")
innerHTML.bind({
onSwitch: noop
})(doc.querySelector("p"), p)
t.equals(doc.querySelector("p").className, "", "classname set correctly")
t.end()
})
tape("test replaceNode switch", function(t) {
var replaceNode = switches.replaceNode

View File

@@ -4,13 +4,14 @@ var extend = require("../../../lib/util/extend")
tape("test extend method", function(t) {
var obj = {one: 1, two: 2}
var extended = extend({}, obj, {two: "two", three: 3})
t.notEqual(obj, extended, "extended object isn't the original object")
t.notSame(obj, extended, "extended object doesn't have the same values as original object")
t.notSame(obj.two, extended.two, "extended object value overwrites value from original object")
extended = extend(null)
t.equals(extended, null, "passing null returns null")
t.end()
})

9
tests/lib/util/noop.js Normal file
View File

@@ -0,0 +1,9 @@
var tape = require("tape")
var noop = require("../../../lib/util/noop")
tape("test noop function", function(t) {
t.equal(typeof noop, "function", "noop is a function")
t.equal(noop(), undefined, "noop() returns nothing")
t.end()
})