OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library XHRTaskTest; |
| 6 |
| 7 import 'dart:async'; |
| 8 import 'dart:convert'; |
| 9 import 'dart:html'; |
| 10 import 'dart:typed_data'; |
| 11 import 'package:unittest/html_individual_config.dart'; |
| 12 import 'package:unittest/unittest.dart'; |
| 13 |
| 14 main() { |
| 15 useHtmlIndividualConfiguration(); |
| 16 |
| 17 // Cache blocker is a workaround for: |
| 18 // https://code.google.com/p/dart/issues/detail?id=11834 |
| 19 var cacheBlocker = new DateTime.now().millisecondsSinceEpoch; |
| 20 var url = '/root_dart/tests/html/xhr_cross_origin_data.txt?' |
| 21 'cacheBlock=$cacheBlocker'; |
| 22 |
| 23 var urlExpando = new Expando(); |
| 24 |
| 25 Function buildCreateTaskHandler(List log, List tasks) { |
| 26 Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone, |
| 27 TaskCreate create, TaskSpecification spec) { |
| 28 if (spec is HttpRequestTaskSpecification) { |
| 29 var url = spec.url; |
| 30 var method = spec.method; |
| 31 var withCredentials = spec.withCredentials; |
| 32 var responseType = spec.responseType; |
| 33 var mimeType = spec.mimeType; |
| 34 var data = spec.sendData; |
| 35 |
| 36 log.add("request $url"); |
| 37 var dataLog = data is List<int> ? "binary ${data.length}" : "$data"; |
| 38 log.add(" method: $method withCredentials: $withCredentials " |
| 39 "responseType: $responseType mimeType: $mimeType data: $dataLog"); |
| 40 var task = parent.createTask(zone, create, spec); |
| 41 urlExpando[task] = url; |
| 42 tasks.add(task); |
| 43 return task; |
| 44 } |
| 45 if (spec is HttpRequestSendTaskSpecification) { |
| 46 var data = spec.sendData; |
| 47 var dataLog = data is List<int> ? "binary ${data.length}" : "$data"; |
| 48 log.add("http-request (no info), data: $dataLog"); |
| 49 var task = parent.createTask(zone, create, spec); |
| 50 tasks.add(task); |
| 51 urlExpando[task] = "unknown"; |
| 52 return task; |
| 53 } |
| 54 if (spec is EventSubscriptionSpecification) { |
| 55 EventSubscriptionSpecification eventSpec = spec; |
| 56 if (eventSpec.target is HttpRequest) { |
| 57 HttpRequest target = eventSpec.target; |
| 58 log.add("event listener on http-request ${eventSpec.eventType}"); |
| 59 if (eventSpec.eventType == "readystatechange") { |
| 60 var oldOnData = eventSpec.onData; |
| 61 spec = eventSpec.replace(onData: (event) { |
| 62 oldOnData(event); |
| 63 if (target.readyState == HttpRequest.DONE) { |
| 64 log.add("unknown request done"); |
| 65 } |
| 66 }); |
| 67 } |
| 68 } |
| 69 } |
| 70 return parent.createTask(zone, create, spec); |
| 71 } |
| 72 |
| 73 return createTaskHandler; |
| 74 } |
| 75 |
| 76 Function buildRunTaskHandler(List log, List tasks) { |
| 77 void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, |
| 78 TaskRun run, Object task, Object arg) { |
| 79 if (tasks.contains(task)) { |
| 80 var url = urlExpando[task]; |
| 81 if (arg is Error || arg is Exception) { |
| 82 log.add("failed $url"); |
| 83 } else { |
| 84 if (arg is ProgressEvent) { |
| 85 log.add("success $url with progress-event"); |
| 86 } else if (arg is HttpRequest){ |
| 87 log.add("success $url with http-request"); |
| 88 } else { |
| 89 log.add("success $url (unknown arg)"); |
| 90 } |
| 91 } |
| 92 } |
| 93 parent.runTask(zone, run, task, arg); |
| 94 } |
| 95 |
| 96 return runTaskHandler; |
| 97 } |
| 98 |
| 99 Future<List> runWithLogging(fun) async { |
| 100 var log = []; |
| 101 var tasks = []; |
| 102 await runZoned(fun, zoneSpecification: new ZoneSpecification( |
| 103 createTask: buildCreateTaskHandler(log, tasks), |
| 104 runTask: buildRunTaskHandler(log, tasks))); |
| 105 return log; |
| 106 } |
| 107 |
| 108 void validate200Response(xhr) { |
| 109 expect(xhr.status, equals(200)); |
| 110 var data = JSON.decode(xhr.responseText); |
| 111 expect(data, contains('feed')); |
| 112 expect(data['feed'], contains('entry')); |
| 113 expect(data, isMap); |
| 114 } |
| 115 |
| 116 void validate404(xhr) { |
| 117 expect(xhr.status, equals(404)); |
| 118 // We cannot say much about xhr.responseText, most HTTP servers will |
| 119 // include an HTML page explaining the error to a human. |
| 120 String responseText = xhr.responseText; |
| 121 expect(responseText, isNotNull); |
| 122 } |
| 123 |
| 124 group('xhr', () { |
| 125 test('XHR No file', () async { |
| 126 var log = await runWithLogging(() { |
| 127 var completer = new Completer(); |
| 128 HttpRequest xhr = new HttpRequest(); |
| 129 xhr.open("GET", "NonExistingFile", async: true); |
| 130 xhr.onReadyStateChange.listen(expectAsyncUntil((event) { |
| 131 if (xhr.readyState == HttpRequest.DONE) { |
| 132 validate404(xhr); |
| 133 completer.complete("done"); |
| 134 } |
| 135 }, () => xhr.readyState == HttpRequest.DONE)); |
| 136 xhr.send(); |
| 137 return completer.future; |
| 138 }); |
| 139 expect(log, equals([ |
| 140 'event listener on http-request readystatechange', |
| 141 'http-request (no info), data: null', |
| 142 'unknown request done' |
| 143 ])); |
| 144 }); |
| 145 |
| 146 test('XHR_file', () async { |
| 147 var log = await runWithLogging(() { |
| 148 var completer = new Completer(); |
| 149 var loadEndCalled = false; |
| 150 |
| 151 var xhr = new HttpRequest(); |
| 152 xhr.open('GET', url, async: true); |
| 153 xhr.onReadyStateChange.listen(expectAsyncUntil((e) { |
| 154 if (xhr.readyState == HttpRequest.DONE) { |
| 155 validate200Response(xhr); |
| 156 |
| 157 Timer.run(expectAsync(() { |
| 158 expect(loadEndCalled, HttpRequest.supportsLoadEndEvent); |
| 159 completer.complete("done"); |
| 160 })); |
| 161 } |
| 162 }, () => xhr.readyState == HttpRequest.DONE)); |
| 163 |
| 164 xhr.onLoadEnd.listen((ProgressEvent e) { |
| 165 loadEndCalled = true; |
| 166 }); |
| 167 xhr.send(); |
| 168 return completer.future; |
| 169 }); |
| 170 expect(log, equals([ |
| 171 'event listener on http-request readystatechange', |
| 172 'event listener on http-request loadend', |
| 173 'http-request (no info), data: null', |
| 174 'unknown request done' |
| 175 ])); |
| 176 }); |
| 177 |
| 178 test('XHR.request No file', () async { |
| 179 var log = await runWithLogging(() { |
| 180 var completer = new Completer(); |
| 181 HttpRequest.request('NonExistingFile').then( |
| 182 (_) { fail('Request should not have succeeded.'); }, |
| 183 onError: expectAsync((error) { |
| 184 var xhr = error.target; |
| 185 expect(xhr.readyState, equals(HttpRequest.DONE)); |
| 186 validate404(xhr); |
| 187 completer.complete('done'); |
| 188 })); |
| 189 return completer.future; |
| 190 }); |
| 191 expect(log, equals([ |
| 192 'request NonExistingFile', |
| 193 ' method: null withCredentials: null responseType: null ' |
| 194 'mimeType: null data: null', |
| 195 'event listener on http-request load', |
| 196 'event listener on http-request error', |
| 197 'success NonExistingFile with progress-event' |
| 198 ])); |
| 199 }); |
| 200 |
| 201 test('XHR.request file', () async { |
| 202 var log = await runWithLogging(() { |
| 203 var completer = new Completer(); |
| 204 HttpRequest.request(url).then(expectAsync((xhr) { |
| 205 expect(xhr.readyState, equals(HttpRequest.DONE)); |
| 206 validate200Response(xhr); |
| 207 completer.complete('done'); |
| 208 })); |
| 209 return completer.future; |
| 210 }); |
| 211 expect(log, equals([ |
| 212 'request $url', |
| 213 ' method: null withCredentials: null responseType: null ' |
| 214 'mimeType: null data: null', |
| 215 'event listener on http-request load', |
| 216 'event listener on http-request error', |
| 217 'success $url with http-request' |
| 218 ])); |
| 219 }); |
| 220 |
| 221 test('XHR.request onProgress', () async { |
| 222 var log = await runWithLogging(() { |
| 223 var completer = new Completer(); |
| 224 var progressCalled = false; |
| 225 HttpRequest.request(url, |
| 226 onProgress: (_) { |
| 227 progressCalled = true; |
| 228 }).then(expectAsync( |
| 229 (xhr) { |
| 230 expect(xhr.readyState, equals(HttpRequest.DONE)); |
| 231 expect(progressCalled, HttpRequest.supportsProgressEvent); |
| 232 validate200Response(xhr); |
| 233 completer.complete("done"); |
| 234 })); |
| 235 return completer.future; |
| 236 }); |
| 237 expect(log, equals([ |
| 238 'request $url', |
| 239 ' method: null withCredentials: null responseType: null ' |
| 240 'mimeType: null data: null', |
| 241 'event listener on http-request progress', |
| 242 'event listener on http-request load', |
| 243 'event listener on http-request error', |
| 244 'success $url with http-request' |
| 245 ])); |
| 246 }); |
| 247 |
| 248 test('XHR.request withCredentials No file', () async { |
| 249 var log = await runWithLogging(() { |
| 250 var completer = new Completer(); |
| 251 HttpRequest.request('NonExistingFile', withCredentials: true).then( |
| 252 (_) { fail('Request should not have succeeded.'); }, |
| 253 onError: expectAsync((error) { |
| 254 var xhr = error.target; |
| 255 expect(xhr.readyState, equals(HttpRequest.DONE)); |
| 256 validate404(xhr); |
| 257 completer.complete("done"); |
| 258 })); |
| 259 return completer.future; |
| 260 }); |
| 261 expect(log, equals([ |
| 262 'request NonExistingFile', |
| 263 ' method: null withCredentials: true responseType: null ' |
| 264 'mimeType: null data: null', |
| 265 'event listener on http-request load', |
| 266 'event listener on http-request error', |
| 267 'success NonExistingFile with progress-event' |
| 268 ])); |
| 269 }); |
| 270 |
| 271 |
| 272 test('XHR.request withCredentials file', () async { |
| 273 var log = await runWithLogging(() { |
| 274 var completer = new Completer(); |
| 275 HttpRequest.request(url, withCredentials: true).then(expectAsync((xhr) { |
| 276 expect(xhr.readyState, equals(HttpRequest.DONE)); |
| 277 validate200Response(xhr); |
| 278 completer.complete("done"); |
| 279 })); |
| 280 return completer.future; |
| 281 }); |
| 282 expect(log, equals([ |
| 283 'request $url', |
| 284 ' method: null withCredentials: true responseType: null ' |
| 285 'mimeType: null data: null', |
| 286 'event listener on http-request load', |
| 287 'event listener on http-request error', |
| 288 'success $url with http-request' |
| 289 ])); |
| 290 }); |
| 291 |
| 292 test('XHR.getString file', () async { |
| 293 var log = await runWithLogging(() { |
| 294 return HttpRequest.getString(url).then(expectAsync((str) {})); |
| 295 }); |
| 296 expect(log, equals([ |
| 297 'request $url', |
| 298 ' method: null withCredentials: null responseType: null ' |
| 299 'mimeType: null data: null', |
| 300 'event listener on http-request load', |
| 301 'event listener on http-request error', |
| 302 'success $url with http-request' |
| 303 ])); |
| 304 }); |
| 305 |
| 306 test('XHR.getString No file', () async { |
| 307 var log = await runWithLogging(() { |
| 308 return HttpRequest.getString('NonExistingFile').then( |
| 309 (_) { fail('Succeeded for non-existing file.'); }, |
| 310 onError: expectAsync((error) { |
| 311 validate404(error.target); |
| 312 })); |
| 313 }); |
| 314 expect(log, equals([ |
| 315 'request NonExistingFile', |
| 316 ' method: null withCredentials: null responseType: null ' |
| 317 'mimeType: null data: null', |
| 318 'event listener on http-request load', |
| 319 'event listener on http-request error', |
| 320 'success NonExistingFile with progress-event' |
| 321 ])); |
| 322 }); |
| 323 |
| 324 test('XHR.request responseType arraybuffer', () async { |
| 325 if (Platform.supportsTypedData) { |
| 326 var log = await runWithLogging(() { |
| 327 return HttpRequest.request(url, responseType: 'arraybuffer', |
| 328 requestHeaders: {'Content-Type': 'text/xml'}).then( |
| 329 expectAsync((xhr) { |
| 330 expect(xhr.status, equals(200)); |
| 331 var byteBuffer = xhr.response; |
| 332 expect(byteBuffer, new isInstanceOf<ByteBuffer>()); |
| 333 expect(byteBuffer, isNotNull); |
| 334 })); |
| 335 }); |
| 336 expect(log, equals([ |
| 337 'request $url', |
| 338 ' method: null withCredentials: null responseType: arraybuffer ' |
| 339 'mimeType: null data: null', |
| 340 'event listener on http-request load', |
| 341 'event listener on http-request error', |
| 342 'success $url with http-request' |
| 343 ])); |
| 344 }; |
| 345 }); |
| 346 |
| 347 test('overrideMimeType', () async { |
| 348 var expectation = |
| 349 HttpRequest.supportsOverrideMimeType ? returnsNormally : throws; |
| 350 |
| 351 var log = await runWithLogging(() { |
| 352 var completer = new Completer(); |
| 353 expect(() { |
| 354 HttpRequest.request(url, mimeType: 'application/binary') |
| 355 .whenComplete(completer.complete); |
| 356 }, expectation); |
| 357 return completer.future; |
| 358 }); |
| 359 expect(log, equals([ |
| 360 'request $url', |
| 361 ' method: null withCredentials: null responseType: null ' |
| 362 'mimeType: application/binary data: null', |
| 363 'event listener on http-request load', |
| 364 'event listener on http-request error', |
| 365 'success $url with http-request' |
| 366 ])); |
| 367 }); |
| 368 |
| 369 if (Platform.supportsTypedData) { |
| 370 test('xhr upload', () async { |
| 371 var log = await runWithLogging(() { |
| 372 var xhr = new HttpRequest(); |
| 373 var progressCalled = false; |
| 374 xhr.upload.onProgress.listen((e) { |
| 375 progressCalled = true; |
| 376 }); |
| 377 |
| 378 xhr.open('POST', |
| 379 '${window.location.protocol}//${window.location.host}/echo'); |
| 380 |
| 381 // 10MB of payload data w/ a bit of data to make sure it |
| 382 // doesn't get compressed to nil. |
| 383 var data = new Uint8List(1 * 1024 * 1024); |
| 384 for (var i = 0; i < data.length; ++i) { |
| 385 data[i] = i & 0xFF; |
| 386 } |
| 387 xhr.send(new Uint8List.view(data.buffer)); |
| 388 |
| 389 return xhr.onLoad.first.then((_) { |
| 390 expect( |
| 391 progressCalled, isTrue, reason: 'onProgress should be fired'); |
| 392 }); |
| 393 }); |
| 394 expect(log, equals([ |
| 395 'http-request (no info), data: binary 1048576', |
| 396 'event listener on http-request load', |
| 397 ])); |
| 398 }); |
| 399 } |
| 400 |
| 401 test('xhr postFormData', () async { |
| 402 var url = '${window.location.protocol}//${window.location.host}/echo'; |
| 403 var log = await runWithLogging(() { |
| 404 var data = { 'name': 'John', 'time': '2 pm'}; |
| 405 |
| 406 var parts = []; |
| 407 for (var key in data.keys) { |
| 408 parts.add('${Uri.encodeQueryComponent(key)}=' |
| 409 '${Uri.encodeQueryComponent(data[key])}'); |
| 410 } |
| 411 var encodedData = parts.join('&'); |
| 412 |
| 413 return HttpRequest.postFormData(url, data).then((xhr) { |
| 414 expect(xhr.responseText, encodedData); |
| 415 }); |
| 416 }); |
| 417 expect(log, equals([ |
| 418 'request $url', |
| 419 ' method: POST withCredentials: null responseType: null ' |
| 420 'mimeType: null data: name=John&time=2+pm', |
| 421 'event listener on http-request load', |
| 422 'event listener on http-request error', |
| 423 'success $url with http-request' |
| 424 ])); |
| 425 }); |
| 426 }); |
| 427 |
| 428 group('xhr_requestBlob', () { |
| 429 test('XHR.request responseType blob', () async { |
| 430 if (Platform.supportsTypedData) { |
| 431 var log = await runWithLogging(() { |
| 432 return HttpRequest.request(url, responseType: 'blob').then( |
| 433 (xhr) { |
| 434 expect(xhr.status, equals(200)); |
| 435 var blob = xhr.response; |
| 436 expect(blob is Blob, isTrue); |
| 437 expect(blob, isNotNull); |
| 438 }); |
| 439 }); |
| 440 expect(log, equals([ |
| 441 'request $url', |
| 442 ' method: null withCredentials: null responseType: blob ' |
| 443 'mimeType: null data: null', |
| 444 'event listener on http-request load', |
| 445 'event listener on http-request error', |
| 446 'success $url with http-request' |
| 447 ])); |
| 448 } |
| 449 }); |
| 450 }); |
| 451 |
| 452 group('json', () { |
| 453 test('xhr responseType json', () async { |
| 454 var url = '${window.location.protocol}//${window.location.host}/echo'; |
| 455 var log = await runWithLogging(() { |
| 456 var completer = new Completer(); |
| 457 var data = { |
| 458 'key': 'value', |
| 459 'a': 'b', |
| 460 'one': 2, |
| 461 }; |
| 462 |
| 463 HttpRequest.request(url, |
| 464 method: 'POST', |
| 465 sendData: JSON.encode(data), |
| 466 responseType: 'json').then( |
| 467 expectAsync((xhr) { |
| 468 expect(xhr.status, equals(200)); |
| 469 var json = xhr.response; |
| 470 expect(json, equals(data)); |
| 471 completer.complete("done"); |
| 472 })); |
| 473 return completer.future; |
| 474 }); |
| 475 expect(log, equals([ |
| 476 'request $url', |
| 477 ' method: POST withCredentials: null responseType: json mimeType: null' |
| 478 ' data: {"key":"value","a":"b","one":2}', |
| 479 'event listener on http-request load', |
| 480 'event listener on http-request error', |
| 481 'success $url with http-request' |
| 482 ])); |
| 483 }); |
| 484 }); |
| 485 |
| 486 group('headers', () { |
| 487 test('xhr responseHeaders', () async { |
| 488 var log = await runWithLogging(() { |
| 489 return HttpRequest.request(url).then( |
| 490 (xhr) { |
| 491 var contentTypeHeader = xhr.responseHeaders['content-type']; |
| 492 expect(contentTypeHeader, isNotNull); |
| 493 // Should be like: 'text/plain; charset=utf-8' |
| 494 expect(contentTypeHeader.contains('text/plain'), isTrue); |
| 495 expect(contentTypeHeader.contains('charset=utf-8'), isTrue); |
| 496 }); |
| 497 }); |
| 498 expect(log, equals([ |
| 499 'request $url', |
| 500 ' method: null withCredentials: null responseType: null' |
| 501 ' mimeType: null data: null', |
| 502 'event listener on http-request load', |
| 503 'event listener on http-request error', |
| 504 'success $url with http-request' |
| 505 ])); |
| 506 }); |
| 507 }); |
| 508 } |
OLD | NEW |