Fix bugs and add tests #145

Merged
BehindTheMath merged 11 commits from fix/bugs-and-add-tests into master 2018-04-09 22:36:33 -05:00
22 changed files with 478 additions and 153 deletions

View File

@@ -445,10 +445,14 @@ Enables verbose mode. Useful to debug page layout differences.
When set to true, clicking on a link that points to the current URL will trigger a full page reload.
The default is `false`, so clicking on such a link will do nothing.
When set to `false`, clicking on such a link will cause Pjax to load the
current page like any page.
If you want to add some custom behavior, add a click listener to the link,
and set `preventDefault` to true, to prevent Pjax from receiving the event.
Note: this must be done before Pjax is instantiated. Otherwise, Pjax's
event handler will be called first, and preventDefault() won't be called yet.
Here is some sample code:
```js
@@ -465,6 +469,8 @@ Here is some sample code:
}
})
}
var pjax = new Pjax()
```
(Note that if `cacheBust` is set to true, the code that checks if the href

View File

@@ -11,7 +11,7 @@
<h1>Index</h1>
Hello.
Go to <a href="page2.html" class="js-Pjax">Page 2</a> or <a href="page3.html" class="js-Pjax">Page 3</a> and view your console to see Pjax events.
Clicking on <a href="index.html">this page</a> will just reload the page entirely.
Clicking on <a href="index.html">this page</a> will do nothing.
<h2>Manual URL loading</h2>

14
index.d.ts vendored
View File

@@ -168,9 +168,23 @@ declare namespace Pjax {
* will not work, due to the query string appended to force a cache bust).
*/
currentUrlFullReload: boolean;
/**
* Hold the information to make an XHR request.
*/
requestOptions?: {
requestUrl?: string;
requestMethod?: string;
requestParams?: IRequestParams[];
}
}
export type Switch = (oldEl: Element, newEl: Element, options?: IOptions, switchesOptions?: StringKeyedObject) => void;
export interface IRequestParams {
name: string,
value: string
}
}
interface StringKeyedObject<T = any> {

View File

@@ -13,6 +13,7 @@ module.exports = function(el) {
script.type = "text/javascript"
/* istanbul ignore if */
if (src !== "") {
script.src = src
script.async = false // force synchronous loading of peripheral JS
@@ -23,6 +24,7 @@ module.exports = function(el) {
script.appendChild(document.createTextNode(code))
}
catch (e) {
/* istanbul ignore next */
// old IEs have funky script nodes
script.text = code
}
@@ -31,7 +33,7 @@ module.exports = function(el) {
// execute
parent.appendChild(script)
// avoid pollution only in head or body tags
if (["head", "body"].indexOf(parent.tagName.toLowerCase()) > 0) {
if (parent instanceof HTMLHeadElement || parent instanceof HTMLBodyElement) {
parent.removeChild(script)
}

View File

@@ -9,16 +9,7 @@ module.exports = function(options) {
options.switches = options.switches || {}
options.switchesOptions = options.switchesOptions || {}
options.history = options.history || true
options.analytics = (typeof options.analytics === "function" || options.analytics === false) ?
options.analytics :
function() {
if (window._gaq) {
_gaq.push(["_trackPageview"])
}
if (window.ga) {
ga("send", "pageview", {page: location.pathname, title: document.title})
}
}
options.analytics = (typeof options.analytics === "function" || options.analytics === false) ? options.analytics : defaultAnalytics
options.scrollTo = (typeof options.scrollTo === "undefined") ? 0 : options.scrollTo
options.scrollRestoration = (typeof options.scrollRestoration !== "undefined") ? options.scrollRestoration : true
options.cacheBust = (typeof options.cacheBust === "undefined") ? true : options.cacheBust
@@ -38,3 +29,13 @@ module.exports = function(options) {
return options
}
/* istanbul ignore next */
function defaultAnalytics() {
if (window._gaq) {
_gaq.push(["_trackPageview"])
}
if (window.ga) {
ga("send", "pageview", {page: location.pathname, title: document.title})
}
}

View File

@@ -1,9 +1,13 @@
var on = require("../events/on")
var clone = require("../util/clone")
var attrClick = "data-pjax-click-state"
var attrState = "data-pjax-state"
var formAction = function(el, event) {
if (isDefaultPrevented(event)) {
return
}
// Since loadUrl modifies options and we may add our own modifications below,
// clone it so the changes don't persist
var options = clone(this.options)
@@ -19,27 +23,9 @@ var formAction = function(el, event) {
var virtLinkElement = document.createElement("a")
virtLinkElement.setAttribute("href", options.requestOptions.requestUrl)
// Ignore external links.
if (virtLinkElement.protocol !== window.location.protocol || virtLinkElement.host !== window.location.host) {
el.setAttribute(attrClick, "external")
return
}
// Ignore click if we are on an anchor on the same page
if (virtLinkElement.pathname === window.location.pathname && virtLinkElement.hash.length > 0) {
el.setAttribute(attrClick, "anchor-present")
return
}
// Ignore empty anchor "foo.html#"
if (virtLinkElement.href === window.location.href.split("#")[0] + "#") {
el.setAttribute(attrClick, "anchor-empty")
return
}
// if declared as a full reload, just normally submit the form
if (options.currentUrlFullReload) {
el.setAttribute(attrClick, "reload")
var attrValue = checkIfShouldAbort(virtLinkElement, options)
if (attrValue) {
el.setAttribute(attrState, attrValue)
return
}
@@ -59,12 +45,34 @@ var formAction = function(el, event) {
}
}
el.setAttribute(attrClick, "submit")
el.setAttribute(attrState, "submit")
options.triggerElement = el
this.loadUrl(virtLinkElement.href, options)
}
function checkIfShouldAbort(virtLinkElement, options) {
// Ignore external links.
if (virtLinkElement.protocol !== window.location.protocol || virtLinkElement.host !== window.location.host) {
return "external"
}
// Ignore click if we are on an anchor on the same page
if (virtLinkElement.hash && virtLinkElement.href.replace(virtLinkElement.hash, "") === window.location.href.replace(location.hash, "")) {
return "anchor"
}
// Ignore empty anchor "foo.html#"
if (virtLinkElement.href === window.location.href.split("#")[0] + "#") {
return "anchor-empty"
}
// if declared as a full reload, just normally submit the form
if (options.currentUrlFullReload && virtLinkElement.href === window.location.href.split("#")[0]) {
return "reload"
}
}
var isDefaultPrevented = function(event) {
return event.defaultPrevented || event.returnValue === false
}
@@ -72,19 +80,13 @@ var isDefaultPrevented = function(event) {
module.exports = function(el) {
var that = this
on(el, "submit", function(event) {
if (isDefaultPrevented(event)) {
return
}
el.setAttribute(attrState, "")
on(el, "submit", function(event) {
formAction.call(that, el, event)
})
on(el, "keyup", function(event) {
if (isDefaultPrevented(event)) {
return
}
if (event.keyCode === 13) {
formAction.call(that, el, event)
}

View File

@@ -1,44 +1,20 @@
var on = require("../events/on")
var clone = require("../util/clone")
var attrClick = "data-pjax-click-state"
var attrKey = "data-pjax-keyup-state"
var attrState = "data-pjax-state"
var linkAction = function(el, event) {
if (isDefaultPrevented(event)) {
return
}
// Since loadUrl modifies options and we may add our own modifications below,
// clone it so the changes don't persist
var options = clone(this.options)
// Dont break browser special behavior on links (like page in new window)
if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
el.setAttribute(attrClick, "modifier")
return
}
// we do test on href now to prevent unexpected behavior if for some reason
// user have href that can be dynamically updated
// Ignore external links.
if (el.protocol !== window.location.protocol || el.host !== window.location.host) {
el.setAttribute(attrClick, "external")
return
}
// Ignore click if we are on an anchor on the same page
if (el.pathname === window.location.pathname && el.hash.length > 0) {
el.setAttribute(attrClick, "anchor-present")
return
}
// Ignore anchors on the same page (keep native behavior)
if (el.hash && el.href.replace(el.hash, "") === window.location.href.replace(location.hash, "")) {
el.setAttribute(attrClick, "anchor")
return
}
// Ignore empty anchor "foo.html#"
if (el.href === window.location.href.split("#")[0] + "#") {
el.setAttribute(attrClick, "anchor-empty")
var attrValue = checkIfShouldAbort(el, event)
if (attrValue) {
el.setAttribute(attrState, attrValue)
return
}
@@ -49,17 +25,42 @@ var linkAction = function(el, event) {
this.options.currentUrlFullReload &&
el.href === window.location.href.split("#")[0]
) {
el.setAttribute(attrClick, "reload")
el.setAttribute(attrState, "reload")
this.reload()
return
}
el.setAttribute(attrClick, "load")
el.setAttribute(attrState, "load")
options.triggerElement = el
this.loadUrl(el.href, options)
}
function checkIfShouldAbort(el, event) {
// Dont break browser special behavior on links (like page in new window)
if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
return "modifier"
}
// we do test on href now to prevent unexpected behavior if for some reason
// user have href that can be dynamically updated
// Ignore external links.
if (el.protocol !== window.location.protocol || el.host !== window.location.host) {
return "external"
}
// Ignore anchors on the same page (keep native behavior)
if (el.hash && el.href.replace(el.hash, "") === window.location.href.replace(location.hash, "")) {
return "anchor"
}
// Ignore empty anchor "foo.html#"
if (el.href === window.location.href.split("#")[0] + "#") {
return "anchor-empty"
}
}
var isDefaultPrevented = function(event) {
return event.defaultPrevented || event.returnValue === false
}
@@ -67,25 +68,13 @@ var isDefaultPrevented = function(event) {
module.exports = function(el) {
var that = this
on(el, "click", function(event) {
if (isDefaultPrevented(event)) {
return
}
el.setAttribute(attrState, "")
on(el, "click", function(event) {
linkAction.call(that, el, event)
})
on(el, "keyup", function(event) {
if (isDefaultPrevented(event)) {
return
}
// Dont break browser special behavior on links (like page in new window)
if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
el.setAttribute(attrKey, "modifier")
return
}
if (event.keyCode === 13) {
linkAction.call(that, el, event)
}

View File

@@ -1,15 +1,17 @@
var attrState = "data-pjax-state"
robinnorth commented 2018-04-09 15:06:00 -05:00 (Migrated from github.com)
Review

This, and line 12 could be abstracted to use var attrState = "data-pjax-state", as in attach-link

This, and line 12 could be abstracted to use `var attrState = "data-pjax-state"`, as in `attach-link`
BehindTheMath commented 2018-04-09 15:18:15 -05:00 (Migrated from github.com)
Review

True.

True.
module.exports = function(el) {
switch (el.tagName.toLowerCase()) {
case "a":
// only attach link if el does not already have link attached
if (!el.hasAttribute("data-pjax-click-state")) {
if (!el.hasAttribute(attrState)) {
this.attachLink(el)
}
break
case "form":
// only attach link if el does not already have link attached
if (!el.hasAttribute("data-pjax-click-state")) {
if (!el.hasAttribute(attrState)) {
this.attachForm(el)
}
break

View File

@@ -15,7 +15,7 @@ module.exports = function(location, options, callback) {
if (request.status === 200) {
callback(request.responseText, request, location)
}
else {
else if (request.status !== 0) {
callback(null, request, location)
}
}
@@ -65,7 +65,7 @@ module.exports = function(location, options, callback) {
// Send the proper header information for POST forms
if (requestPayload && requestMethod === "POST") {
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
}
request.send(requestPayload)

View File

@@ -8,7 +8,14 @@ module.exports = {
innerHTML: function(oldEl, newEl) {
oldEl.innerHTML = newEl.innerHTML
if (newEl.className === "") {
oldEl.removeAttribute("class")
}
else {
oldEl.className = newEl.className
}
this.onSwitch()
},

View File

@@ -1,4 +1,5 @@
module.exports = function(obj) {
/* istanbul ignore if */
if (null === obj || "object" !== typeof obj) {
return obj
}

View File

@@ -1,6 +1,6 @@
module.exports = function(target) {
if (target == null) {
return target
return null
}
var to = Object(target)

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()
})