Fix bugs and add tests #145
@@ -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
|
||||
|
||||
@@ -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
14
index.d.ts
vendored
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
// Don’t 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) {
|
||||
// Don’t 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
|
||||
}
|
||||
|
||||
// Don’t 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)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
var attrState = "data-pjax-state"
|
||||
|
|
||||
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -8,7 +8,14 @@ module.exports = {
|
||||
|
||||
innerHTML: function(oldEl, newEl) {
|
||||
oldEl.innerHTML = newEl.innerHTML
|
||||
oldEl.className = newEl.className
|
||||
|
||||
if (newEl.className === "") {
|
||||
oldEl.removeAttribute("class")
|
||||
}
|
||||
else {
|
||||
oldEl.className = newEl.className
|
||||
}
|
||||
|
||||
this.onSwitch()
|
||||
},
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = function(obj) {
|
||||
/* istanbul ignore if */
|
||||
if (null === obj || "object" !== typeof obj) {
|
||||
return obj
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = function(target) {
|
||||
if (target == null) {
|
||||
return target
|
||||
return null
|
||||
}
|
||||
|
||||
var to = Object(target)
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
9
tests/lib/util/noop.js
Normal 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()
|
||||
})
|
||||
Reference in New Issue
Block a user
This, and line 12 could be abstracted to use
var attrState = "data-pjax-state", as inattach-linkTrue.