blob: 03a36bd5f417f9b0269b2be83c116cb305e7e6ea [file] [log] [blame]
<!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>