From e4a3b0fe9f61d8f2b854604e2aa90ac5bcfbbb06 Mon Sep 17 00:00:00 2001 From: Alex Hill Date: Mon, 9 Mar 2015 17:00:42 +0800 Subject: [PATCH 1/7] Test for correct HTTP_REFERER after failure --- test/app.rb | 9 +++++++++ test/unit/pjax_fallback.js | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/test/app.rb b/test/app.rb index bb44a582..9d78b21b 100644 --- a/test/app.rb +++ b/test/app.rb @@ -90,6 +90,15 @@ def title(str) erb :boom_sans_pjax, :layout => false end +get '/referer_timeout.html' do + if pjax? + sleep 1 + erb :referer, :layout => false + else + erb :referer + end +end + get '/:page.html' do erb :"#{params[:page]}", :layout => !pjax? end diff --git a/test/unit/pjax_fallback.js b/test/unit/pjax_fallback.js index 539e45f8..58eaa98c 100644 --- a/test/unit/pjax_fallback.js +++ b/test/unit/pjax_fallback.js @@ -103,6 +103,21 @@ asyncTest("sends correct HTTP referer"+s, function() { }) }) +asyncTest("sends correct HTTP referer after failed request"+s, function() { + var frame = this.frame + + $('iframe')[0].onload = function() { + var referer = frame.document.getElementById("referer").textContent + ok(referer.match("/home.html"), referer) + start() + } + + frame.$.pjax({ + url: "/referer_timeout.html", + container: "#main" + }) +}) + asyncTest("scrolls to top of the page"+s, function() { var frame = this.frame From 00b886f06a1d3465bc95cce8354a33ef52817526 Mon Sep 17 00:00:00 2001 From: Alex Hill Date: Mon, 9 Mar 2015 17:08:45 +0800 Subject: [PATCH 2/7] Test history when clicking back during load --- test/unit/pjax.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/unit/pjax.js b/test/unit/pjax.js index e8932ac3..e4420c2b 100644 --- a/test/unit/pjax.js +++ b/test/unit/pjax.js @@ -889,6 +889,55 @@ if ($.support.pjax) { }) }) + asyncTest("clicking back while loading maintains history", function() { + var frame = this.frame + + equal(frame.location.pathname, "/home.html") + equal(frame.document.title, "Home") + + frame.$("#main").on('pjax:timeout', function(e) { + e.preventDefault(); + }) + + frame.$("#main").one('pjax:complete', function() { + + equal(frame.location.pathname, "/hello.html") + equal(frame.document.title, "Hello") + + frame.$("#main").one('pjax:send', function() { + + // don't use goBack here, because pjax:end isn't triggered + // when clicking back while loading + + frame.history.back(); + + setTimeout(function() { + equal(frame.location.pathname, "/home.html") + equal(frame.document.title, "Home") + + frame.history.forward() + + setTimeout(function() { + equal(frame.location.pathname, "/hello.html") + equal(frame.document.title, "Hello") + start() + }, 250) + + }, 1500) + }) + + frame.$.pjax({ + url: "timeout.html", + container: "#main" + }) + }) + + frame.$.pjax({ + url: "hello.html", + container: "#main" + }) + }) + asyncTest("popstate going back to page", function() { var frame = this.frame From d0031b13266da5d2e428277faf1b907f7d1d7ab5 Mon Sep 17 00:00:00 2001 From: Alex Hill Date: Mon, 9 Mar 2015 16:59:48 +0800 Subject: [PATCH 3/7] Test that URL only changes on PJAX success In preparation for the next commit which will delay pushState until pjax success, here we remove assertions that the address bar changes immediately, and add a failing test of the desired behaviour. --- test/unit/pjax.js | 33 +++++++++++++++++++++------------ test/unit/pjax_fallback.js | 9 ++------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/test/unit/pjax.js b/test/unit/pjax.js index e4420c2b..f4f05e31 100644 --- a/test/unit/pjax.js +++ b/test/unit/pjax.js @@ -322,10 +322,6 @@ if ($.support.pjax) { data: { foo: 1, bar: 2 }, container: "#main" }) - - // URL is set immediately - equal(frame.location.pathname, "/env.html") - equal(frame.location.search, "?foo=1&bar=2") }) asyncTest("GET data is merged into query string", function() { @@ -345,10 +341,6 @@ if ($.support.pjax) { data: { bar: 2 }, container: "#main" }) - - // URL is set immediately - equal(frame.location.pathname, "/env.html") - equal(frame.location.search, "?foo=1&bar=2") }) asyncTest("mixed containers", function() { @@ -745,9 +737,6 @@ if ($.support.pjax) { container: "#main" }) - equal(frame.location.pathname, "/timeout.html") - equal(frame.location.hash, "#hello") - this.iframe.onload = function() { equal(frame.$("#main p").html(), "SLOW DOWN!") equal(frame.location.pathname, "/timeout.html") @@ -817,7 +806,7 @@ if ($.support.pjax) { var frame = this.frame frame.$("#main").on("pjax:complete", function() { - equal(frame.location.pathname, "/boom.html") + equal(frame.location.pathname, "/home.html") start() }) frame.$("#main").on("pjax:error", function(event, xhr) { @@ -835,6 +824,26 @@ if ($.support.pjax) { }) }) + asyncTest("address bar only updates on success", function() { + var frame = this.frame + + equal(frame.location.pathname, "/home.html") + + frame.$("#main").on('pjax:send', function() { + equal(frame.location.pathname, "/home.html") + }) + + frame.$("#main").on('pjax:end', function() { + equal(frame.location.pathname, "/hello.html") + start() + }) + + frame.$.pjax({ + url: "hello.html", + container: "#main" + }) + }) + function goBack(frame, callback) { setTimeout(function() { frame.$("#main").one("pjax:end", callback) diff --git a/test/unit/pjax_fallback.js b/test/unit/pjax_fallback.js index 58eaa98c..8b4d8607 100644 --- a/test/unit/pjax_fallback.js +++ b/test/unit/pjax_fallback.js @@ -154,13 +154,8 @@ asyncTest("scrolls to anchor at top page"+s, function() { container: "#main" }) - if (disabled) { - equal(frame.location.pathname, "/home.html") - equal(frame.location.hash, "") - } else { - equal(frame.location.pathname, "/anchor.html") - equal(frame.location.hash, "#top") - } + equal(frame.location.pathname, "/home.html") + equal(frame.location.hash, "") }) asyncTest("empty anchor doesn't scroll page"+s, function() { From c1106b2d14a9b986791e1813406ce94d589817d9 Mon Sep 17 00:00:00 2001 From: Alex Hill Date: Mon, 9 Mar 2015 17:04:27 +0800 Subject: [PATCH 4/7] Add basic browser history test --- test/unit/pjax_fallback.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/unit/pjax_fallback.js b/test/unit/pjax_fallback.js index 8b4d8607..fb92137e 100644 --- a/test/unit/pjax_fallback.js +++ b/test/unit/pjax_fallback.js @@ -118,6 +118,34 @@ asyncTest("sends correct HTTP referer after failed request"+s, function() { }) }) +asyncTest("adds entry to browser history"+s, function() { + var frame = this.frame + var count = 0 + + frame.onpopstate = function() { + window.iframeLoad(frame) + } + + this.loaded = function() { + count++ + + if (count == 1) { + equal(frame.location.pathname, "/hello.html") + ok(frame.history.length > 1) + frame.history.back() + } else if (count == 2) { + equal(frame.location.pathname, "/home.html") + frame.history.forward() + start() + } + } + + frame.$.pjax({ + url: "hello.html", + container: "#main" + }) +}) + asyncTest("scrolls to top of the page"+s, function() { var frame = this.frame From 830ae334afe026c33236e9dd8dc12c4795768ceb Mon Sep 17 00:00:00 2001 From: Alex Hill Date: Mon, 9 Mar 2015 17:08:08 +0800 Subject: [PATCH 5/7] Make XHR cancellation test more robust --- test/unit/pjax.js | 60 +++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/test/unit/pjax.js b/test/unit/pjax.js index f4f05e31..b30e06a1 100644 --- a/test/unit/pjax.js +++ b/test/unit/pjax.js @@ -861,41 +861,55 @@ if ($.support.pjax) { asyncTest("clicking back while loading cancels XHR", function() { var frame = this.frame - frame.$('#main').on('pjax:timeout', function(event) { - event.preventDefault() + equal(frame.location.pathname, "/home.html") + equal(frame.document.title, "Home") + + frame.$("#main").on('pjax:timeout', function(e) { + e.preventDefault(); }) - frame.$("#main").one('pjax:send', function() { + frame.$("#main").one('pjax:complete', function() { - // Check that our request is aborted (need to check - // how robust this is across browsers) - frame.$("#main").one('pjax:complete', function(e, xhr, textStatus) { - equal(xhr.status, 0) - equal(textStatus, 'abort') - }) + equal(frame.location.pathname, "/hello.html") + equal(frame.document.title, "Hello") - setTimeout(function() { - frame.history.back() - }, 250) + frame.$("#main").one('pjax:send', function() { - // Make sure the URL and content remain the same after the - // XHR would have arrived (delay on timeout.html is 1s) - setTimeout(function() { - var afterBackLocation = frame.location.pathname - var afterBackTitle = frame.document.title + // don't use goBack here, because pjax:end isn't triggered + // when clicking back while loading + + frame.$("#main").one('pjax:complete', function(e, xhr, textStatus) { + equal(xhr.status, 0); + equal(textStatus, 'abort') + }) + + frame.history.back(); + // Make sure the URL and content remain the same after the + // XHR would have arrived (delay on timeout.html is 1s) setTimeout(function() { - equal(frame.location.pathname, afterBackLocation) - equal(frame.document.title, afterBackTitle) - start() - }, 1000) - }, 500) + var afterBackLocation = frame.location.pathname + var afterBackTitle = frame.document.title + + setTimeout(function() { + equal(frame.location.pathname, afterBackLocation) + equal(frame.document.title, afterBackTitle) + start() + }, 1000) + }, 500) + }) + + frame.$.pjax({ + url: "timeout.html", + container: "#main" + }) }) frame.$.pjax({ - url: "timeout.html", + url: "hello.html", container: "#main" }) + }) asyncTest("clicking back while loading maintains history", function() { From 186c198cdfa13c27879a3ba9358a388978ba4551 Mon Sep 17 00:00:00 2001 From: Alex Hill Date: Mon, 9 Mar 2015 17:08:58 +0800 Subject: [PATCH 6/7] Delay cachePush and pushState until XHR success --- jquery.pjax.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/jquery.pjax.js b/jquery.pjax.js index f9810cee..23f1d2d4 100644 --- a/jquery.pjax.js +++ b/jquery.pjax.js @@ -244,7 +244,7 @@ function pjax(options) { var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options]) if (options.type == 'GET' && textStatus !== 'abort' && allowed) { - locationReplace(container.url) + window.location.assign(container.url) } } @@ -269,16 +269,20 @@ function pjax(options) { // If there is a layout version mismatch, hard load the new url if (currentVersion && latestVersion && currentVersion !== latestVersion) { - locationReplace(container.url) + window.location.assign(container.url) return } // If the new response is missing a body, hard load the page if (!container.contents) { - locationReplace(container.url) + window.location.assign(container.url) return } + if (options.push) { + cachePush(pjax.state.id, cloneContents(context)) + } + pjax.state = { id: options.id || uniqueId(), url: container.url, @@ -288,7 +292,9 @@ function pjax(options) { timeout: options.timeout } - if (options.push || options.replace) { + if (options.push) { + window.history.pushState(pjax.state, container.title, container.url) + } else if (options.replace) { window.history.replaceState(pjax.state, container.title, container.url) } @@ -355,13 +361,6 @@ function pjax(options) { var xhr = pjax.xhr = $.ajax(options) if (xhr.readyState > 0) { - if (options.push && !options.replace) { - // Cache current container element before replacing it - cachePush(pjax.state.id, cloneContents(context)) - - window.history.pushState(null, "", options.requestUrl) - } - fire('pjax:start', [xhr, options]) fire('pjax:send', [xhr, options]) } From b2a0e92d82b86426bf0ad27f244810848cb8c0e4 Mon Sep 17 00:00:00 2001 From: Alex Hill Date: Wed, 15 Apr 2015 20:37:06 +0800 Subject: [PATCH 7/7] Move cachePop to after pjax:start and pjax:popstate --- jquery.pjax.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/jquery.pjax.js b/jquery.pjax.js index 23f1d2d4..fb606d53 100644 --- a/jquery.pjax.js +++ b/jquery.pjax.js @@ -446,12 +446,6 @@ function onPjaxPopstate(event) { if (container.length) { var contents = cacheMapping[state.id] - if (previousState) { - // Cache current container before replacement and inform the - // cache which direction the history shifted. - cachePop(direction, previousState.id, cloneContents(container)) - } - var popstateEvent = $.Event('pjax:popstate', { state: state, direction: direction @@ -468,9 +462,19 @@ function onPjaxPopstate(event) { scrollTo: false } + function doCachePop() { + if (previousState) { + cachePop(direction, previousState.id, cloneContents(container)) + } + } + if (contents) { container.trigger('pjax:start', [null, options]) + // Only touch the cache after pjax:start for consistency with the + // sequence of events in pjax(). + doCachePop() + pjax.state = state if (state.title) document.title = state.title var beforeReplaceEvent = $.Event('pjax:beforeReplace', { @@ -482,6 +486,7 @@ function onPjaxPopstate(event) { container.trigger('pjax:end', [null, options]) } else { + doCachePop() pjax(options) }