1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/test/test_xhr_progressevents.html Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,327 @@ 1.4 +<!DOCTYPE HTML> 1.5 +<html> 1.6 +<head> 1.7 + <title>Test for XMLHttpRequest Progress Events</title> 1.8 + <script type="text/javascript" src="/MochiKit/packed.js"></script> 1.9 + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> 1.10 + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 1.11 +</head> 1.12 +<body onload="gen.next();"> 1.13 +<pre id=l></pre> 1.14 +<script type="application/javascript;version=1.7"> 1.15 +SimpleTest.waitForExplicitFinish(); 1.16 + 1.17 +var gen = runTests(); 1.18 + 1.19 +function log(s) { 1.20 + // Uncomment these to get debugging information 1.21 + /* 1.22 + document.getElementById("l").textContent += s + "\n"; 1.23 + dump(s + "\n"); 1.24 + */ 1.25 +} 1.26 + 1.27 +function getEvent(e) { 1.28 + log("got event: " + e.type + " (" + e.target.readyState + ")"); 1.29 + gen.send(e); 1.30 +} 1.31 + 1.32 +function startsWith(a, b) { 1.33 + return a.substr(0, b.length) === b; 1.34 +} 1.35 + 1.36 +function updateProgress(e, data, testName) { 1.37 + var test = " while running " + testName; 1.38 + is(e.type, "progress", "event type" + test); 1.39 + 1.40 + let response; 1.41 + if (data.nodata) { 1.42 + is(e.target.response, null, "response should be null" + test); 1.43 + response = null; 1.44 + } 1.45 + else if (data.text) { 1.46 + is(typeof e.target.response, "string", "response should be a string" + test); 1.47 + response = e.target.response; 1.48 + } 1.49 + else if (data.blob) { 1.50 + ok(e.target.response instanceof Blob, "response should be a Blob" + test); 1.51 + response = e.target.response; 1.52 + } 1.53 + else { 1.54 + ok(e.target.response instanceof ArrayBuffer, "response should be an ArrayBuffer" + test); 1.55 + response = bufferToString(e.target.response); 1.56 + } 1.57 + is(e.target.response, e.target.response, "reflexivity should hold" + test); 1.58 + 1.59 + if (!data.nodata && !data.encoded) { 1.60 + if (data.blob) { 1.61 + is(e.loaded, response.size, "event.loaded matches response size" + test); 1.62 + } 1.63 + else if (!data.chunked) { 1.64 + is(e.loaded, response.length, "event.loaded matches response size" + test); 1.65 + } 1.66 + else { 1.67 + is(e.loaded - data.receivedBytes, response.length, 1.68 + "event.loaded grew by response size" + test); 1.69 + } 1.70 + } 1.71 + ok(e.loaded > data.receivedBytes, "event.loaded increased" + test); 1.72 + ok(e.loaded - data.receivedBytes <= data.pendingBytes, 1.73 + "event.loaded didn't increase too much" + test); 1.74 + 1.75 + if (!data.nodata && !data.blob) { 1.76 + var newData; 1.77 + ok(startsWith(response, data.receivedResult), 1.78 + "response strictly grew" + test); 1.79 + newData = response.substr(data.receivedResult.length); 1.80 + 1.81 + if (!data.encoded) { 1.82 + ok(newData.length > 0, "sanity check for progress" + test); 1.83 + } 1.84 + ok(startsWith(data.pendingResult, newData), "new data matches expected" + test); 1.85 + } 1.86 + 1.87 + is(e.lengthComputable, "total" in data, "lengthComputable" + test); 1.88 + if ("total" in data) { 1.89 + is(e.total, data.total, "total" + test); 1.90 + } 1.91 + 1.92 + if (!data.nodata && !data.blob) { 1.93 + data.pendingResult = data.pendingResult.substr(newData.length); 1.94 + } 1.95 + data.pendingBytes -= e.loaded - data.receivedBytes; 1.96 + data.receivedResult = response; 1.97 + data.receivedBytes = e.loaded; 1.98 +} 1.99 + 1.100 +function sendData(s) { 1.101 + var xhr = new XMLHttpRequest(); 1.102 + xhr.open("POST", "progressserver.sjs?send"); 1.103 + xhr.sendAsBinary(s); 1.104 +} 1.105 + 1.106 +function closeConn() { 1.107 + log("in closeConn"); 1.108 + var xhr = new XMLHttpRequest(); 1.109 + xhr.open("POST", "progressserver.sjs?close"); 1.110 + xhr.send(); 1.111 + return xhr; 1.112 +} 1.113 + 1.114 +var longString = "long"; 1.115 +while(longString.length < 65536) 1.116 + longString += longString; 1.117 + 1.118 +function utf8encode(s) { 1.119 + return unescape(encodeURIComponent(s)); 1.120 +} 1.121 + 1.122 +function bufferToString(buffer) { 1.123 + return String.fromCharCode.apply(String, new Uint8Array(buffer)); 1.124 +} 1.125 + 1.126 +function runTests() { 1.127 + var xhr = new XMLHttpRequest(); 1.128 + xhr.onprogress = xhr.onload = xhr.onerror = xhr.onreadystatechange = xhr.onloadend = getEvent; 1.129 + 1.130 + var responseTypes = [{ type: "text", text: true }, 1.131 + { type: "arraybuffer", text: false, nodata: true }, 1.132 + { type: "blob", text: false, nodata: true, blob: true }, 1.133 + { type: "moz-blob", text: false, nodata: false, blob: true }, 1.134 + { type: "document", text: true, nodata: true }, 1.135 + { type: "json", text: true, nodata: true }, 1.136 + { type: "", text: true }, 1.137 + { type: "moz-chunked-text", text: true, chunked: true }, 1.138 + { type: "moz-chunked-arraybuffer", text: false, chunked: true }, 1.139 + ]; 1.140 + var responseType; 1.141 + var fileExpectedResult = ""; 1.142 + for (var i = 0; i < 65536; i++) { 1.143 + fileExpectedResult += String.fromCharCode(i & 255); 1.144 + } 1.145 + while (responseType = responseTypes.shift()) { 1.146 + let tests = [{ open: "Content-Type=text/plain", name: "simple test" }, 1.147 + { data: "hello world" }, 1.148 + { data: "\u0000\u0001\u0002\u0003" }, 1.149 + { data: longString }, 1.150 + { data: "x" }, 1.151 + { close: true }, 1.152 + { open: "Content-Type=text/plain&Content-Length=20", name: "with length", total: 20 }, 1.153 + // 5 bytes from the "ready" in the open step 1.154 + { data: "abcde" }, 1.155 + { data: "0123456789" }, 1.156 + { close: true }, 1.157 + { open: "Content-Type=application/xml", name: "without length, as xml" }, 1.158 + { data: "<out>" }, 1.159 + { data: "text" }, 1.160 + { data: "</foo>invalid" }, 1.161 + { close: true }, 1.162 + { open: "Content-Type=text/plain;charset%3dutf-8", name: "utf8 data", encoded: true }, 1.163 + { data: utf8encode("räksmörgås"), utf16: "räksmörgås" }, 1.164 + { data: utf8encode("Å").substr(0,1), utf16: "" }, 1.165 + { data: utf8encode("Å").substr(1), utf16: "Å" }, 1.166 + { data: utf8encode("aöb").substr(0,2), utf16: "a" }, 1.167 + { data: utf8encode("aöb").substr(2), utf16: "öb" }, 1.168 + { data: utf8encode("a\u867Eb").substr(0,3), utf16: "a" }, 1.169 + { data: utf8encode("a\u867Eb").substr(3,1), utf16: "\u867E" }, 1.170 + { data: utf8encode("a\u867Eb").substr(4), utf16: "b" }, 1.171 + { close: true }, 1.172 + ]; 1.173 + if (responseType.blob) { 1.174 + tests.push({ file: "file_XHR_binary2.bin", name: "cacheable data", total: 65536 }, 1.175 + { close: true }, 1.176 + { file: "file_XHR_binary2.bin", name: "cached data", total: 65536 }, 1.177 + { close: true }); 1.178 + } 1.179 + let testState = { index: 0 }; 1.180 + 1.181 + for (let i = 0; i < tests.length; ++i) { 1.182 + let test = tests[i]; 1.183 + testState.index++; 1.184 + if ("open" in test || "file" in test) { 1.185 + log("opening " + testState.name); 1.186 + testState = { name: test.name + " for " + responseType.type, 1.187 + index: 0, 1.188 + pendingResult: "ready", 1.189 + pendingBytes: 5, 1.190 + receivedResult: "", 1.191 + receivedBytes: 0, 1.192 + total: test.total, 1.193 + encoded: test.encoded, 1.194 + nodata: responseType.nodata, 1.195 + chunked: responseType.chunked, 1.196 + text: responseType.text, 1.197 + blob: responseType.blob, 1.198 + file: test.file }; 1.199 + 1.200 + xhr.onreadystatechange = null; 1.201 + if (testState.file) 1.202 + xhr.open("GET", test.file); 1.203 + else 1.204 + xhr.open("POST", "progressserver.sjs?open&" + test.open); 1.205 + xhr.responseType = responseType.type; 1.206 + xhr.send("ready"); 1.207 + xhr.onreadystatechange = getEvent; 1.208 + 1.209 + let e = yield undefined; 1.210 + is(e.type, "readystatechange", "should readystate to headers-received starting " + testState.name); 1.211 + is(xhr.readyState, xhr.HEADERS_RECEIVED, "should be in state HEADERS_RECEIVED starting " + testState.name); 1.212 + 1.213 + e = yield undefined; 1.214 + is(e.type, "readystatechange", "should readystate to loading starting " + testState.name); 1.215 + is(xhr.readyState, xhr.LOADING, "should be in state LOADING starting " + testState.name); 1.216 + if (typeof testState.total == "undefined") 1.217 + delete testState.total; 1.218 + } 1.219 + if ("file" in test) { 1.220 + testState.pendingBytes = testState.total; 1.221 + testState.pendingResult = fileExpectedResult; 1.222 + } 1.223 + if ("close" in test) { 1.224 + log("closing"); 1.225 + let xhrClose; 1.226 + if (!testState.file) 1.227 + xhrClose = closeConn(); 1.228 + 1.229 + e = yield undefined; 1.230 + is(e.type, "readystatechange", "should readystate to done closing " + testState.name); 1.231 + is(xhr.readyState, xhr.DONE, "should be in state DONE closing " + testState.name); 1.232 + log("readystate to 4"); 1.233 + 1.234 + if (responseType.chunked) { 1.235 + xhr.responseType; 1.236 + is(xhr.response, null, "chunked data has null response for " + testState.name); 1.237 + } 1.238 + 1.239 + e = yield undefined; 1.240 + is(e.type, "load", "should fire load closing " + testState.name); 1.241 + is(e.lengthComputable, true, "length should be computable during load closing " + testState.name); 1.242 + log("got load"); 1.243 + 1.244 + if (responseType.chunked) { 1.245 + is(xhr.response, null, "chunked data has null response for " + testState.name); 1.246 + } 1.247 + 1.248 + e = yield undefined; 1.249 + is(e.type, "loadend", "should fire loadend closing " + testState.name); 1.250 + is(e.lengthComputable, true, "length should be computable during loadend closing " + testState.name); 1.251 + log("got loadend"); 1.252 + 1.253 + // if we closed the connection using an explicit request, make sure that goes through before 1.254 + // running the next test in order to avoid reordered requests from closing the wrong 1.255 + // connection. 1.256 + if (xhrClose && xhrClose.readyState != xhrClose.DONE) { 1.257 + log("wait for closeConn to finish"); 1.258 + xhrClose.onloadend = getEvent; 1.259 + yield undefined; 1.260 + is(xhrClose.readyState, xhrClose.DONE, "closeConn finished"); 1.261 + } 1.262 + 1.263 + if (responseType.chunked) { 1.264 + is(xhr.response, null, "chunked data has null response for " + testState.name); 1.265 + } 1.266 + 1.267 + if (!testState.nodata && !responseType.blob || responseType.chunked) { 1.268 + // This branch intentionally left blank 1.269 + // Under these conditions we check the response during updateProgress 1.270 + } 1.271 + else if (responseType.type === "arraybuffer") { 1.272 + is(bufferToString(xhr.response), testState.pendingResult, 1.273 + "full response for " + testState.name); 1.274 + } 1.275 + else if (responseType.blob) { 1.276 + let reader = new FileReader; 1.277 + reader.readAsBinaryString(xhr.response); 1.278 + reader.onloadend = getEvent; 1.279 + yield undefined; 1.280 + 1.281 + is(reader.result, testState.pendingResult, 1.282 + "full response in blob for " + testState.name); 1.283 + } 1.284 + 1.285 + testState.name = ""; 1.286 + } 1.287 + if ("data" in test) { 1.288 + log("sending"); 1.289 + if (responseType.text) { 1.290 + testState.pendingResult += "utf16" in test ? test.utf16 : test.data; 1.291 + } 1.292 + else { 1.293 + testState.pendingResult += test.data; 1.294 + } 1.295 + testState.pendingBytes = test.data.length; 1.296 + sendData(test.data); 1.297 + } 1.298 + 1.299 + while(testState.pendingBytes) { 1.300 + log("waiting for more bytes: " + testState.pendingBytes); 1.301 + e = yield undefined; 1.302 + // Readystate can fire several times between each progress event. 1.303 + if (e.type === "readystatechange") 1.304 + continue; 1.305 + 1.306 + updateProgress(e, testState, "data for " + testState.name + "[" + testState.index + "]"); 1.307 + if (responseType.chunked) { 1.308 + testState.receivedResult = ""; 1.309 + } 1.310 + } 1.311 + 1.312 + if (!testState.nodata && !testState.blob) { 1.313 + is(testState.pendingResult, "", 1.314 + "should have consumed the expected result"); 1.315 + } 1.316 + 1.317 + log("done with this test"); 1.318 + } 1.319 + 1.320 + is(testState.name, "", "forgot to close last test"); 1.321 + } 1.322 + 1.323 + SimpleTest.finish(); 1.324 + yield undefined; 1.325 +} 1.326 + 1.327 +</script> 1.328 + 1.329 +</body> 1.330 +</html>