| <!doctype html> |
| <html> |
| <head> |
| <meta content="text/html; charset=utf-8" http-equiv="content-type" /> |
| <link rel="author" title="Microsoft" href="http://www.microsoft.com/" /> |
| <link rel="help" href="http://dev.w3.org/html5/spec/history.html" /> |
| <title>HTML5 History Test Cases</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| </head> |
| <body> |
| <div id="log"></div> |
| |
| <!-- Use this iframe to test url changes so that the base url does not change. Their document does not matter. --> |
| <iframe id="testframe1" style="display:none" src="./404.html"></iframe> |
| <iframe id="testframe2" style="display:none" src="./404.html"></iframe> |
| |
| <script type="text/javascript"> |
| var testCollection; |
| var testIndex = 0; |
| var testframe1 = document.getElementById("testframe1"); |
| var testframe2 = document.getElementById("testframe2"); |
| |
| setup(function() |
| { |
| testCollection = [ |
| function() { |
| test(function() { |
| assert_inherits(window, "onpopstate", "window inherits'onpopstate' event "); |
| }, "onpopstate in window"); |
| }, |
| function() { |
| test(function() { |
| assert_inherits(window.history, "pushState", "history inherits property 'pushState'"); |
| assert_equals(window.history.pushState.constructor, Function, "pushState is a function"); |
| }, "history.pushState is present"); |
| }, |
| function() { |
| test(function() { |
| assert_inherits(window.history, "replaceState", "history inherits property 'replaceState'"); |
| assert_equals(window.history.replaceState.constructor, Function, "replaceState is a function"); |
| }, "history.replaceState is present"); |
| }, |
| function() { |
| test(function() { |
| assert_inherits(window.history, "state", "history inherits property 'state'"); |
| }, "history.state is present"); |
| }, |
| function() { |
| test(function() { |
| assert_equals(window.history.state, null, "history.state initialized to null"); |
| }, "history.state is initialized to null"); |
| }, |
| |
| function() { |
| test(function() { |
| var length = history.length; |
| history.pushState(null,null); |
| assert_equals(history.length, length+1, "history.length should be incremented by one"); |
| }, "history.pushState increments history.length"); |
| }, |
| |
| function() { |
| var t = async_test("history.pushState clears forward entries"); |
| t.step(function() { |
| var length = history.length; |
| //push some extra entries into the session history |
| history.pushState(null,null); |
| history.pushState(null, null); |
| history.pushState(null, null); |
| |
| //there should now be three extra |
| assert_equals(history.length, length+3, "Three additional travel entries add to history.length"); |
| |
| //travel back to the entry that the test started on |
| history.back(); |
| history.back(); |
| history.back(); |
| |
| //if the back navs are queued, queue verification task after them |
| queue( |
| t.step_func(function() { |
| //once the .back navigations have completed, push again and verify length is one more than starting value |
| history.pushState(null, null); |
| assert_equals(history.length, length+1, "History.length should now only be one more than original value"); |
| t.done(); |
| }) |
| ); |
| }); |
| }, |
| |
| function() { |
| test(function() { |
| testframe1.contentWindow.history.pushState(null,null, "test-pushstate-url"); |
| assert_equals(getPageName(testframe1.contentWindow.location.href), "test-pushstate-url", "iframe1 has the pushed url"); |
| }, "history.pushState accepts a third parameter 'url' and uses it to alter location"); |
| }, |
| function() { |
| test(function() { |
| var oldurl = testframe1.contentWindow.location.href.toString(); |
| var pagename = getPageName(oldurl); |
| //form a new absolute url (with protocol, host, etc) with "absolute-page" as the name of the page |
| var newurl = oldurl.replace(pagename, "absolute-page"); |
| |
| testframe1.contentWindow.history.pushState(null,null, newurl); |
| assert_equals(testframe1.contentWindow.location.href, newurl, "iframe1 has the pushed url correctly"); |
| }, "history.pushState's url parameter can be an absolute url"); |
| }, |
| |
| function() { |
| test(function() { |
| testframe1.contentWindow.history.pushState(null,null, "multiple-pushstate-url1"); |
| testframe2.contentWindow.history.pushState(null,null, "multiple-pushstate-url2"); |
| |
| assert_equals(getPageName(testframe1.contentWindow.location.href), "multiple-pushstate-url1", "iframe1 has the pushed url"); |
| assert_equals(getPageName(testframe2.contentWindow.location.href), "multiple-pushstate-url2", "iframe2 has the pushed url"); |
| }, "history.pushState can modify location object in multiple frames"); |
| }, |
| |
| function() { |
| test(function() { |
| //trigger a security error by replacing the host of the current url with a fake one that is cross-domain |
| var testurl = testframe1.contentWindow.location.href.toString().replace(testframe1.contentWindow.location.host, "fakelocation-push"); |
| assert_throws("SECURITY_ERR", function() { history.pushState(null, null, testurl); }, "Security_Err 18 should be thrown"); |
| }, "history.pushState throws DOMException with code SECURITY_ERR (18)"); |
| }, |
| |
| function() { |
| test(function() { |
| //trigger a data clone error by passing invalid SCA data into the function |
| assert_throws("DATA_CLONE_ERR", function() { history.pushState(document.body, null); }, "pushState should throw an exception DATA_CLONE_ERR with code 25"); |
| }, "history.pushState throws DATA_CLONE_ERR(25) for bad state parameter"); |
| }, |
| |
| function() { |
| test(function() { |
| var length = history.length; |
| history.replaceState(null,null); |
| assert_equals(history.length, length, "history.length should not change"); |
| }, "history.replaceState does not increment history.length"); |
| }, |
| |
| function() { |
| var t = async_test("history.replaceState does not clear forward entries"); |
| t.step(function() { |
| var length = history.length; |
| //push some extra entries into the session history |
| history.pushState(null,null); |
| history.pushState(null, null); |
| history.pushState(null, null); |
| |
| //there should now be three extra |
| assert_equals(history.length, length+3, "Three additional travel entries add to history.length"); |
| |
| //travel back two entries to land in the middle |
| history.back(); |
| history.back(); |
| |
| //if the back navs are queued, queue verification task after them |
| queue( |
| t.step_func(function() { |
| //once the .back navigations have fired, push again and verify length has not changed since the last check |
| history.replaceState(null, null); |
| assert_equals(history.length, length+3, "History.length should still be three more than original value"); |
| t.done(); |
| }) |
| ); |
| }); |
| }, |
| |
| function() { |
| test(function() { |
| testframe1.contentWindow.history.replaceState(null,null, "test-replaceState-url"); |
| assert_equals(getPageName(testframe1.contentWindow.location.href), "test-replaceState-url", "iframe1 has the pushed url"); |
| }, "history.replaceState accepts a third parameter 'url' and uses it to alter location"); |
| }, |
| function() { |
| test(function() { |
| var oldurl = testframe1.contentWindow.location.href.toString(); |
| var pagename = getPageName(oldurl); |
| //form a new absolute url (with protocol, host, etc) with "absolute-page" as the name of the page |
| var newurl = oldurl.replace(pagename, "absolute-page"); |
| |
| testframe1.contentWindow.history.replaceState(null,null, newurl); |
| assert_equals(testframe1.contentWindow.location.href, newurl, "iframe1 has the pushed url correctly"); |
| }, "history.replaceState's url parameter can be an absolute url"); |
| }, |
| |
| function() { |
| test(function() { |
| testframe1.contentWindow.history.replaceState(null,null, "multiple-replaceState-url1"); |
| testframe2.contentWindow.history.replaceState(null,null, "multiple-replaceState-url2"); |
| |
| assert_equals(getPageName(testframe1.contentWindow.location.href), "multiple-replaceState-url1", "iframe1 has the pushed url"); |
| assert_equals(getPageName(testframe2.contentWindow.location.href), "multiple-replaceState-url2", "iframe2 has the pushed url"); |
| }, "history.replaceState can modify location object in multiple frames"); |
| }, |
| |
| function() { |
| test(function() { |
| //trigger a security error by replacing the host of the current url with a fake one that is cross-domain |
| var testurl = testframe1.contentWindow.location.href.toString().replace(testframe1.contentWindow.location.host, "fakelocation-replace"); |
| assert_throws("SECURITY_ERR", function() { history.replaceState(null, null, testurl); }, "Security_Err 18 should be thrown"); |
| }, "history.replaceState throws DOMException with code SECURITY_ERR (18)"); |
| }, |
| |
| function() { |
| test(function() { |
| //trigger a data clone error by passing invalid SCA data into the function |
| assert_throws("DATA_CLONE_ERR", function() {history.replaceState(document.body, null);}, "replaceState should throw an exception DATA_CLONE_ERR with code 25"); |
| }, "history.replaceState throws DATA_CLONE_ERR(25) for bad state parameter"); |
| }, |
| |
| function() { |
| var t = async_test("PopStateEvent fires on Back navigation"); |
| t.step(function() { |
| history.pushState(null, null); |
| history.pushState(null, null); |
| //prepare to end the test as soon as popstate fires |
| onpopstate = function(e) { |
| t.done(); |
| } |
| //go back to fire the popstate event |
| history.back(); |
| }); |
| }, |
| |
| function() { |
| var t = async_test("PopStateEvent fires on Forward navigation"); |
| t.step( function() { |
| onpopstate = null; |
| history.pushState(null, null); |
| history.pushState(null, null); |
| history.back(); |
| //if the back navigation is queued, set up the rest of the test after it is done |
| queue( |
| t.step_func(function() { |
| //prepare to end the test as soon as popstate fires |
| onpopstate = function(e) { |
| t.done(); |
| } |
| //go forward to fire the popstate event |
| history.forward(); |
| }) |
| ); |
| }); |
| }, |
| |
| function() { |
| var t = async_test("PopStateEvent receives state data on Back navigation"); |
| t.step(function() { |
| history.pushState("popstate-data", null); |
| history.pushState(null, null); |
| //prepare the popstate event to validate the data |
| onpopstate = t.step_func(function(e) { |
| assert_equals(e.state, "popstate-data", "State data is passed to the event correctly"); |
| t.done(); |
| }); |
| //go back to fire the popstate event |
| history.back(); |
| }); |
| }, |
| |
| function() { |
| test(function() { |
| history.pushState("pushstate-data", null); |
| //history.state should be set immediately |
| assert_equals(history.state, "pushstate-data", "State data is set correctly"); |
| }, "history.state is set by history.pushState"); |
| }, |
| |
| function() { |
| test(function() { |
| history.replaceState("replacestate-data", null); |
| //history.state should be set immediately |
| assert_equals(history.state, "replacestate-data", "State data is set correctly"); |
| }, "history.state is set by history.replaceState"); |
| }, |
| |
| function() { |
| var t = async_test("history.state changes on navigation"); |
| t.step(function() { |
| history.pushState("state-back1", null); |
| history.pushState("state-back2", null); |
| //precondition |
| assert_equals(history.state, "state-back2", "Verify that history.state is set to a second value"); |
| |
| //set up the popstate event to verify that history.state was changed |
| onpopstate = t.step_func(function(e) { |
| assert_equals(e.state, "state-back1", "Verify that history.state reverted to the first value"); |
| t.done(); |
| }); |
| history.back(); |
| }); |
| }, |
| ]; |
| }, {explicit_done:true, timeout:8000}); |
| |
| //used to get the name of a page within a path |
| // to check correctness of url parameter |
| function getPageName(path) { |
| var path = path || location.pathname; |
| var segments = path.split('/'); |
| return segments[segments.length-1]; |
| } |
| |
| //Callback for result_callback |
| //queues the next test in the array testCollection |
| //serves to make test execution sequential despite asynchronous behaviors |
| function testFinished(test) { |
| if(testIndex < testCollection.length - 1) { |
| //queue the function so that stack remains shallow |
| queue(testCollection[++testIndex]); |
| } else { |
| //when the last test has run, explicitly end test suite |
| done(); |
| } |
| } |
| function queue(func) { |
| //50 allows adequate time for .back and .forward navigations to queue first |
| setTimeout(func, 50); |
| } |
| |
| add_result_callback(testFinished); |
| //start the first test manually |
| queue(testCollection[testIndex]); |
| </script> |
| </body> |
| </html> |