test_xhr_progressevents.html (11075B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <title>Test for XMLHttpRequest Progress Events</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 7 </head> 8 <body onload="gen.next();"> 9 <pre id=l></pre> 10 <script type="application/javascript"> 11 SimpleTest.waitForExplicitFinish(); 12 13 var gen = runTests(); 14 15 function log() { 16 // Uncomment these to get debugging information 17 /* 18 document.getElementById("l").textContent += s + "\n"; 19 dump(s + "\n"); 20 */ 21 } 22 23 function getEvent(e) { 24 log("got event: " + e.type + " (" + e.target.readyState + ")"); 25 gen.next(e); 26 } 27 28 function startsWith(a, b) { 29 return a.substr(0, b.length) === b; 30 } 31 32 function updateProgress(e, data, testName) { 33 var test = " while running " + testName; 34 is(e.type, "progress", "event type" + test); 35 36 let response; 37 if (data.nodata) { 38 is(e.target.response, null, "response should be null" + test); 39 response = null; 40 } 41 else if (data.text) { 42 is(typeof e.target.response, "string", "response should be a string" + test); 43 response = e.target.response; 44 } 45 else if (data.blob) { 46 ok(e.target.response instanceof Blob, "response should be a Blob" + test); 47 response = e.target.response; 48 } 49 else { 50 ok(e.target.response instanceof ArrayBuffer, "response should be an ArrayBuffer" + test); 51 response = bufferToString(e.target.response); 52 } 53 is(e.target.response, e.target.response, "reflexivity should hold" + test); 54 55 if (!data.nodata && !data.encoded) { 56 if (data.blob) { 57 is(e.loaded, response.size, "event.loaded matches response size" + test); 58 } 59 else { 60 is(e.loaded, response.length, "event.loaded matches response size" + test); 61 } 62 } 63 ok(e.loaded > data.receivedBytes, "event.loaded increased" + test); 64 ok(e.loaded - data.receivedBytes <= data.pendingBytes, 65 "event.loaded didn't increase too much" + test); 66 67 if (!data.nodata && !data.blob) { 68 var newData; 69 ok(startsWith(response, data.receivedResult), 70 "response strictly grew" + test); 71 newData = response.substr(data.receivedResult.length); 72 73 if (!data.encoded) { 74 ok(newData.length, "sanity check for progress" + test); 75 } 76 ok(startsWith(data.pendingResult, newData), "new data matches expected" + test); 77 } 78 79 is(e.lengthComputable, "total" in data, "lengthComputable" + test); 80 if ("total" in data) { 81 is(e.total, data.total, "total" + test); 82 } 83 84 if (!data.nodata && !data.blob) { 85 data.pendingResult = data.pendingResult.substr(newData.length); 86 } 87 data.pendingBytes -= e.loaded - data.receivedBytes; 88 data.receivedResult = response; 89 data.receivedBytes = e.loaded; 90 } 91 92 function sendData(s) { 93 var xhr = new XMLHttpRequest(); 94 xhr.open("POST", "progressserver.sjs?send"); 95 // The Blob constructor encodes String elements as UTF-8; 96 // for straight bytes, manually convert to ArrayBuffer first 97 var buffer = new Uint8Array(s.length); 98 for (var i = 0; i < s.length; ++i) { 99 buffer[i] = s.charCodeAt(i) & 0xff; 100 }; 101 xhr.send(new Blob([buffer])); 102 } 103 104 function closeConn() { 105 log("in closeConn"); 106 var xhr = new XMLHttpRequest(); 107 xhr.open("POST", "progressserver.sjs?close"); 108 xhr.send(); 109 return xhr; 110 } 111 112 var longString = "long"; 113 while(longString.length < 65536) 114 longString += longString; 115 116 function utf8encode(s) { 117 return unescape(encodeURIComponent(s)); 118 } 119 120 function bufferToString(buffer) { 121 return String.fromCharCode.apply(String, new Uint8Array(buffer)); 122 } 123 124 function* runTests() { 125 var xhr = new XMLHttpRequest(); 126 xhr.onprogress = xhr.onload = xhr.onerror = xhr.onreadystatechange = xhr.onloadend = getEvent; 127 128 var responseTypes = [{ type: "text", text: true }, 129 { type: "arraybuffer", text: false, nodata: true }, 130 { type: "blob", text: false, nodata: true, blob: true }, 131 { type: "document", text: true, nodata: true }, 132 { type: "json", text: true, nodata: true }, 133 { type: "", text: true }, 134 ]; 135 var responseType; 136 var fileExpectedResult = ""; 137 for (let i = 0; i < 65536; i++) { 138 fileExpectedResult += String.fromCharCode(i & 255); 139 } 140 while ((responseType = responseTypes.shift())) { 141 let tests = [{ open: "Content-Type=text/plain", name: "simple test" }, 142 { data: "hello world" }, 143 { data: "\u0000\u0001\u0002\u0003" }, 144 { data: longString }, 145 { data: "x" }, 146 { close: true }, 147 { open: "Content-Type=text/plain&Content-Length=20", name: "with length", total: 20 }, 148 // 5 bytes from the "ready" in the open step 149 { data: "abcde" }, 150 { data: "0123456789" }, 151 { close: true }, 152 { open: "Content-Type=application/xml", name: "without length, as xml" }, 153 { data: "<out>" }, 154 { data: "text" }, 155 { data: "</foo>invalid" }, 156 { close: true }, 157 { open: "Content-Type=text/plain;charset%3dutf-8", name: "utf8 data", encoded: true }, 158 { data: utf8encode("räksmörgås"), utf16: "räksmörgås" }, 159 { data: utf8encode("Å").substr(0,1), utf16: "" }, 160 { data: utf8encode("Å").substr(1), utf16: "Å" }, 161 { data: utf8encode("aöb").substr(0,2), utf16: "a" }, 162 { data: utf8encode("aöb").substr(2), utf16: "öb" }, 163 { data: utf8encode("a\u867Eb").substr(0,3), utf16: "a" }, 164 { data: utf8encode("a\u867Eb").substr(3,1), utf16: "\u867E" }, 165 { data: utf8encode("a\u867Eb").substr(4), utf16: "b" }, 166 { close: true }, 167 ]; 168 if (responseType.blob) { 169 tests.push({ file: "file_XHR_binary2.bin", name: "cacheable data", total: 65536 }, 170 { close: true }, 171 { file: "file_XHR_binary2.bin", name: "cached data", total: 65536 }, 172 { close: true }); 173 } 174 let testState = { index: 0 }; 175 176 for (let i = 0; i < tests.length; ++i) { 177 let test = tests[i]; 178 testState.index++; 179 if ("open" in test || "file" in test) { 180 log("opening " + testState.name); 181 testState = { name: test.name + " for " + responseType.type, 182 index: 0, 183 pendingResult: "ready", 184 pendingBytes: 5, 185 receivedResult: "", 186 receivedBytes: 0, 187 total: test.total, 188 encoded: test.encoded, 189 nodata: responseType.nodata, 190 text: responseType.text, 191 blob: responseType.blob, 192 file: test.file }; 193 194 xhr.onreadystatechange = null; 195 if (testState.file) 196 xhr.open("GET", test.file); 197 else 198 xhr.open("POST", "progressserver.sjs?open&" + test.open); 199 xhr.responseType = responseType.type; 200 xhr.send("ready"); 201 xhr.onreadystatechange = getEvent; 202 203 let e = yield undefined; 204 is(e.type, "readystatechange", "should readystate to headers-received starting " + testState.name); 205 is(xhr.readyState, xhr.HEADERS_RECEIVED, "should be in state HEADERS_RECEIVED starting " + testState.name); 206 207 e = yield undefined; 208 is(e.type, "readystatechange", "should readystate to loading starting " + testState.name); 209 is(xhr.readyState, xhr.LOADING, "should be in state LOADING starting " + testState.name); 210 if (typeof testState.total == "undefined") 211 delete testState.total; 212 } 213 if ("file" in test) { 214 testState.pendingBytes = testState.total; 215 testState.pendingResult = fileExpectedResult; 216 } 217 if ("close" in test) { 218 log("closing"); 219 let xhrClose; 220 if (!testState.file) 221 xhrClose = closeConn(); 222 223 let e = yield undefined; 224 is(e.type, "readystatechange", "should readystate to done closing " + testState.name); 225 is(xhr.readyState, xhr.DONE, "should be in state DONE closing " + testState.name); 226 log("readystate to 4"); 227 228 e = yield undefined; 229 is(e.type, "load", "should fire load closing " + testState.name); 230 is(e.lengthComputable, e.total != 0, "length should " + (e.total == 0 ? "not " : "") + "be computable during load closing " + testState.name); 231 log("got load"); 232 233 e = yield undefined; 234 is(e.type, "loadend", "should fire loadend closing " + testState.name); 235 is(e.lengthComputable, e.total != 0, "length should " + (e.total == 0 ? "not " : "") + "be computable during loadend closing " + testState.name); 236 log("got loadend"); 237 238 // if we closed the connection using an explicit request, make sure that goes through before 239 // running the next test in order to avoid reordered requests from closing the wrong 240 // connection. 241 if (xhrClose && xhrClose.readyState != xhrClose.DONE) { 242 log("wait for closeConn to finish"); 243 xhrClose.onloadend = getEvent; 244 yield undefined; 245 is(xhrClose.readyState, xhrClose.DONE, "closeConn finished"); 246 } 247 248 if (!testState.nodata && !responseType.blob) { 249 // This branch intentionally left blank 250 // Under these conditions we check the response during updateProgress 251 } 252 else if (responseType.type === "arraybuffer") { 253 is(bufferToString(xhr.response), testState.pendingResult, 254 "full response for " + testState.name); 255 } 256 else if (responseType.blob) { 257 let reader = new FileReader; 258 reader.readAsBinaryString(xhr.response); 259 reader.onloadend = getEvent; 260 yield undefined; 261 262 is(reader.result, testState.pendingResult, 263 "full response in blob for " + testState.name); 264 } 265 266 testState.name = ""; 267 } 268 if ("data" in test) { 269 log("sending"); 270 if (responseType.text) { 271 testState.pendingResult += "utf16" in test ? test.utf16 : test.data; 272 } 273 else { 274 testState.pendingResult += test.data; 275 } 276 testState.pendingBytes = test.data.length; 277 sendData(test.data); 278 } 279 280 while(testState.pendingBytes) { 281 log("waiting for more bytes: " + testState.pendingBytes); 282 let e = yield undefined; 283 // Readystate can fire several times between each progress event. 284 if (e.type === "readystatechange") 285 continue; 286 287 updateProgress(e, testState, "data for " + testState.name + "[" + testState.index + "]"); 288 } 289 290 if (!testState.nodata && !testState.blob) { 291 is(testState.pendingResult, "", 292 "should have consumed the expected result"); 293 } 294 295 log("done with this test"); 296 } 297 298 is(testState.name, "", "forgot to close last test"); 299 } 300 301 SimpleTest.finish(); 302 } 303 304 </script> 305 306 </body> 307 </html>