Source code

Revision control

Other Tools

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(s) {
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 > 0, "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 (var 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
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
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>