UNPKG

19.4 kBJavaScriptView Raw
1module('FileAPI');
2
3(function (){
4 if( !Function.prototype.bind ){
5 Function.prototype.bind = function (ctx){
6 if( !ctx ) {
7 return this;
8 }
9 var fn = this;
10 return function (){
11 return fn.apply(ctx, arguments);
12 };
13 };
14 }
15
16
17 var controllerUrl = 'http://127.0.0.1:8000/upload';
18 var uploadForm = document.forms.upload;
19 var base64_1px_gif = 'R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
20 var browser = (navigator.userAgent.match(/(phantomjs|safari|firefox|chrome)/i) || ['', 'chrome'])[1].toLowerCase();
21
22 QUnit.config.autostart = browser == 'phantomjs';
23
24
25
26
27 FileAPI.event.on(startBtn, 'click', function (){
28 QUnit.start();
29 });
30
31
32 function checkFile(file, name, type, size){
33 equal(file.name, name, 'name');
34 equal(file.type, type, 'type');
35 equal(file.size, size, 'size');
36 }
37
38
39 function loadImage(src, fn){
40 if( src.file ){
41 fn(src.file);
42 }
43 else {
44 var img = document.createElement('img');
45 img.onload = function (){
46 fn(img);
47 };
48 img.src = src;
49 }
50 }
51
52
53 function toCanvas(img){
54 var canvas = document.createElement('canvas');
55 if( img ){
56 canvas.width = img.width || img.videoWidth;
57 canvas.height = img.height || img.videoHeight;
58 var ctx = canvas.getContext('2d');
59 try {
60 ctx.drawImage(img, 0, 0);
61 } catch (err){
62 console.log(err.toString());
63 console.log(err.stack);
64 }
65 }
66 return canvas;
67 }
68
69
70 function imageEqual(left, right, text, callback, delta){
71 loadImage(left, function (left){
72 left.setAttribute('style', 'border: 2px solid red; padding: 2px;');
73 document.body.appendChild(left.cloneNode());
74
75 loadImage(right, function (right){
76 right.setAttribute('style', 'border: 2px solid blue; padding: 2px;');
77 document.body.appendChild(right);
78
79 left = toCanvas(left);
80 right = toCanvas(right);
81
82 if( left.width != right.width ){
83 ok(false, text + ' (width: '+left.width+' != '+right.width+')');
84 }
85 else if( left.height != right.height ){
86 ok(false, text + ' (height: '+left.height+' != '+right.height+')');
87 }
88 else {
89 var state = true, pixels = 0, failPixels = 0;
90 var leftData = left.getContext('2d').getImageData(0, 0, left.width, left.height);
91 var rightData = right.getContext('2d').getImageData(0, 0, left.width, left.height);
92
93 check: for( var y = 0; y < leftData.height; y++ ){
94 for( var x = 0; x < leftData.width; x++ ){
95 var idx = (x + y * leftData.width) * 4;
96 pixels++;
97 if( !pixelEqual(leftData.data, rightData.data, idx) ){
98 failPixels++;
99 // break check;
100 }
101 }
102 }
103
104 ok(failPixels/pixels < (delta || .01), text + ' (fail pixels: '+ (failPixels/pixels) +')');
105 }
106
107 callback();
108 });
109 });
110 }
111
112
113 function pixelEqual(left, right, idx){
114 var dt = 3;
115 return (Math.abs(left[idx] - right[idx]) < dt) && (Math.abs(left[idx+1] - right[idx+1]) < dt) && (Math.abs(left[idx+2] - right[idx+2]) < dt);
116 }
117
118
119 function _checkProgressEvent(evt){
120 var fail = false;
121
122 if( isNaN(evt.loaded / evt.total) ){
123 fail = true;
124 ok(false, "progress: evt.loaded/evt.total - is NaN");
125 }
126
127 if( isNaN(evt.loaded) ){
128 fail = true;
129 ok(false, "progress: evt.loaded - is NaN");
130 }
131
132 if( isNaN(evt.total) ){
133 fail = true;
134 ok(false, "progress: evt.total - is NaN");
135 }
136
137 return fail;
138 }
139
140 console.log('\nStart testing\n');
141
142 test('1px.gif', function (){
143 var file = FileAPI.getFiles(uploadForm['1px_gif'])[0];
144
145 // File
146 checkFile(file, '1px.gif', 'image/gif', 34);
147
148 // File info
149 stop();
150 FileAPI.getInfo(file, function (err, info){
151 start();
152 ok(!err);
153 equal(info.width, 1, 'getInfo.width');
154 equal(info.height, 1, 'getInfo.height');
155 });
156
157
158 if( FileAPI.html5 ){
159 // Read as data
160 stop();
161 FileAPI.readAsDataURL(file, function (evt){
162 if( evt.type == 'load' ){
163 start();
164 equal(evt.result, 'data:image/gif;base64,'+base64_1px_gif, 'dataURL');
165 }
166 });
167
168 // Read as binaryString
169 stop();
170 FileAPI.readAsBinaryString(file, function (evt){
171 if( evt.type == 'load' ){
172 start();
173 equal(evt.result, FileAPI.toBinaryString(base64_1px_gif), 'dataURL');
174 }
175 });
176
177 // Read as image
178 stop();
179 FileAPI.readAsImage(file, function (evt){
180 if( evt.type == 'load' ){
181 start();
182 equal(evt.result.width, 1, 'readAsImage.width');
183 equal(evt.result.height, 1, 'readAsImage.height');
184 }
185 });
186 }
187 });
188
189 test('big.jpg', function (){
190 console.log('Entering big.jpg');
191 var file = FileAPI.getFiles(uploadForm['big.jpg'])[0];
192
193 // File
194 console.log('[big.jpg] before check file');
195 checkFile(file, 'big.jpg', 'image/jpeg', 71674475);
196 console.log('[big.jpg] after check file');
197
198 // File info
199 stop();
200 console.log('[big.jpg] before getInfo');
201 FileAPI.getInfo(file, function (err, info){
202 console.log('[big.jpg] after getInfo');
203 start();
204 ok(!err);
205 equal(info.width, 10000, 'getInfo.width');
206 equal(info.height, 10000, 'getInfo.height');
207 });
208
209 stop();
210 FileAPI.upload({
211 url: controllerUrl,
212 files: { image: file },
213 imageTransform: {
214 width: 1024,
215 height: 768,
216 type: 'image/jpeg'
217 },
218 complete: function (err, res){
219 start();
220 }
221 });
222 });
223
224 test('hello.txt', function (){
225 var file = FileAPI.getFiles(uploadForm['hello.txt'])[0];
226
227 checkFile(file, 'hello.txt', 'text/plain', 15);
228
229 if( FileAPI.html5 ){
230 stop();
231 FileAPI.readAsText(file, function (evt){
232 if( evt.type == 'load' ){
233 start();
234 equal(evt.result, 'Hello FileAPI!\n');
235 }
236 });
237 }
238 });
239
240 test('image.jpg', function (){
241 var file = FileAPI.getFiles(uploadForm['image.jpg'])[0];
242
243 checkFile(file, 'image.jpg', 'image/jpeg', 108338);
244
245 // Check exif
246 stop();
247 FileAPI.getInfo(file, function (err, info){
248 start();
249 equal(info.width, 632, 'width');
250 equal(info.height, 448, 'height');
251 equal(info.exif.Orientation, 6, 'Orientation');
252
253 if( FileAPI.html5 && FileAPI.support.html5 ){
254 equal(info.exif.Artist, 'FileAPI');
255 equal(info.exif.Copyright, 'BSD');
256 }
257 });
258 });
259
260 test('filterFiles', function (){
261 var files = FileAPI.getFiles(uploadForm['multiple']);
262
263 // Filer files
264 stop();
265 FileAPI.filterFiles(files, function (file, info){
266 if( /^image/.test(file.type) ){
267 return file.type == 'image/png';
268 }
269 else {
270 return file.size > 128;
271 }
272 }, function (files, ignor){
273 start();
274 equal(files.length, 2, 'files');
275 equal(ignor.length, 3, 'ignor');
276 })
277 });
278
279 test('upload without files', function (){
280 stop();
281
282 FileAPI.upload({
283 url: controllerUrl,
284 data: { str: 'foo', num: 1, array: [1, 2, 3], object: { foo: 'bar' } },
285 headers: { 'x-foo': 'bar' },
286 complete: function (err, xhr){
287 var res = err ? {} : FileAPI.parseJSON(xhr.responseText).data._REQUEST;
288 var headers = err ? err : FileAPI.parseJSON(xhr.responseText).data.HEADERS;
289
290 start();
291 ok(!err, 'upload done');
292 equal(res.str, 'foo', 'string');
293 equal(res.num, '1', 'number');
294 equal(headers['x-foo'], 'bar', 'headers.x-foo');
295
296 if( !FileAPI.html5 || !FileAPI.support.html5 ){
297 deepEqual(res.array, { "0": '1', "1": '2', "2": '3' }, 'array');
298 }
299 else {
300 deepEqual(res.array, ['1', '2', '3'], 'array');
301 }
302
303 deepEqual(res.object, { foo: 'bar' }, 'object');
304 }
305 })
306 });
307
308 test('upload input', function (){
309 var rnd = Math.random();
310 expect(15);
311
312 stop();
313 FileAPI.upload({
314 url: controllerUrl,
315 data: { foo: 'bar' },
316 headers: { 'x-foo': 'bar', 'x-rnd': rnd },
317 files: uploadForm['1px_gif'],
318 upload: function (){
319 ok(true, 'upload event');
320 },
321 prepare: function (file, options){
322 options.data.bar = 'qux';
323 },
324 fileupload: function (file/**Object*/, xhr/**Object*/, options/**Object*/){
325 equal(file.name, '1px.gif', 'name');
326 equal(options.data.foo, 'bar', 'data.foo');
327 equal(options.data.bar, 'qux', 'data.qux');
328 },
329 filecomplete: function (err, xhr){
330 var res = FileAPI.parseJSON(xhr.responseText);
331
332 equal(res.data._REQUEST.foo, 'bar');
333 equal(res.data._REQUEST.bar, 'qux');
334 equal(res.data.HEADERS['x-foo'], 'bar', 'headers.x-foo');
335 equal(res.data.HEADERS['x-rnd'], rnd, 'headers.x-rnd');
336
337 if( res.data._FILES['1px_gif'] ){
338 var type = res.data._FILES['1px_gif'].type;
339 equal(res.data._FILES['1px_gif'].name, '1px.gif', 'file.name');
340 equal(type, /application/.test(type) ? 'application/octet-stream' : 'image/gif', 'file.type');
341 } else {
342 ok(false, "res.data._FILES['1px_gif'] not found");
343 }
344
345 if( res.images['1px_gif'] ){
346 equal(res.images['1px_gif'].dataURL, 'data:image/gif;base64,' + base64_1px_gif, 'dataURL');
347 equal(res.images['1px_gif'].mime, 'image/gif', 'mime');
348 equal(res.images['1px_gif'].width, 1, 'width');
349 equal(res.images['1px_gif'].height, 1, 'height');
350 } else {
351 ok(false, "res.images['1px_gif'] not found");
352 }
353 },
354 complete: function (err, xhr){
355 ok(true, 'complete event');
356 start();
357 }
358 });
359 });
360
361 test('upload file', function (){
362 var _progressFail = false, _progress = 0;
363 stop();
364
365 FileAPI.upload({
366 url: controllerUrl,
367 files: { text: FileAPI.getFiles(uploadForm['hello.txt']) },
368 progress: function (evt){
369 _progressFail = _progressFail || _checkProgressEvent(evt);
370 if( !_progressFail && (_progress >= evt.loaded) ){
371 _progressFail = true;
372 ok(false, 'progress evt.loaded: '+_progress+' -> '+evt.loaded);
373 }
374 _progress = evt.loaded;
375 },
376 complete: function (err, res){
377 start();
378 var res = FileAPI.parseJSON(res.responseText).data._FILES['text'];
379 equal(res.name, 'hello.txt', 'file.name');
380 equal(res.size, 15, 'file.size');
381 equal(res.type, /application/.test(res.type) ? 'application/octet-stream' : 'text/plain', 'file.type');
382 }
383 });
384 });
385
386 test('multiupload', function (){
387 stop();
388 var
389 _start = 0
390 , _complete = 0
391 , _progress = 0
392 , _progressFail = false
393 , _files = {}
394 ;
395
396 FileAPI.upload({
397 url: controllerUrl,
398 files: uploadForm['multiple'],
399 fileupload: function (){
400 _start++;
401 },
402 filecomplete: function (err, xhr){
403 var file = FileAPI.parseJSON(xhr.responseText).data._FILES.multiple;
404 _complete++;
405 _files[file.name] = file;
406 },
407 progress: function (evt){
408 _progressFail = _progressFail || _checkProgressEvent(evt);
409 if( !_progressFail && (_progress >= evt.loaded) ){
410 _progressFail = true;
411 ok(false, 'progress evt.loaded: '+_progress+' -> '+evt.loaded);
412 }
413 _progress = evt.loaded;
414 },
415 complete: function (err, xhr){
416 start();
417
418 equal(_start, _complete, 'uploaded');
419
420 checkFile(_files['1px.gif'], '1px.gif', 'image/gif', 34);
421 checkFile(_files['dino.png'], 'dino.png', 'image/png', 461003);
422 checkFile(_files['hello.txt'], 'hello.txt', 'text/plain', 15);
423 checkFile(_files['image.jpg'], 'image.jpg', 'image/jpeg', 108338);
424// checkFile(_files['lebowski.json'], 'lebowski.json', 'application/json', 5392);
425 }
426 });
427 });
428
429 FileAPI.html5 && test('upload FileAPI.Image', function (){
430 var file = FileAPI.getFiles(uploadForm['dino.png'])[0];
431 var image = FileAPI.Image(file).rotate(90+360).preview(100);
432 var _progressFail = false,
433 _progress = 0,
434 _fileprogress = 0,
435 _filecomplete,
436 _filecompleteErr
437 ;
438
439 stop();
440 FileAPI.upload({
441 url: controllerUrl,
442 headers: { 'x-foo': 'bar' },
443 files: { image: image },
444 progress: function (evt){
445 _progressFail = _progressFail || _checkProgressEvent(evt);
446
447 if( !_progressFail && (_progress >= evt.loaded) ){
448 _progressFail = true;
449 ok(false, 'progress evt.loaded: '+_progress+' -> '+evt.loaded);
450 }
451 _progress = evt.loaded;
452 },
453 fileprogress: function (evt) {
454 if (_fileprogress < evt.loaded) {
455 _fileprogress = evt.loaded;
456 }
457 },
458 filecomplete: function (err, res){
459 _filecomplete = res.responseText;
460 _filecompleteErr = err;
461 },
462 complete: function (err, res){
463 var json = FileAPI.parseJSON(res.responseText);
464
465 ok(_progress > 0, 'progress event');
466 ok(_fileprogress > 0, 'fileprogress event');
467
468 equal(err, _filecompleteErr, 'filecomplete.err');
469 equal(res.responseText, _filecomplete, 'filecomplete.response');
470
471 equal(json.data.HEADERS['x-foo'], 'bar', 'x-foo');
472
473 imageEqual(json.images.image.dataURL, 'files/samples/'+browser+'-dino-90deg-100x100.png?1', 'dino 90deg 100x100', function (){
474 start();
475 });
476 }
477 });
478 });
479
480 FileAPI.html5 && test('upload + imageTransform (min, max, preview)', function (){
481 var file = FileAPI.getFiles(uploadForm['image.jpg'])[0];
482 var queue = FileAPI.queue(start);
483
484 stop();
485
486 // strategy: 'min'
487 queue.inc();
488 FileAPI.upload({
489 url: controllerUrl,
490 files: { image: file },
491 imageTransform: { width: 100, height: 100, strategy: 'min' },
492 complete: function (err, res){
493 queue.next();
494 var res = FileAPI.parseJSON(res.responseText);
495 equal(res.images['image'].width, 141, 'min.width');
496 equal(res.images['image'].height, 100, 'min.height');
497 }
498 });
499
500 // strategy: 'max'
501 queue.inc();
502 FileAPI.upload({
503 url: controllerUrl,
504 files: { image: file },
505 imageTransform: { width: 100, height: 100, strategy: 'max' },
506 complete: function (err, res){
507 queue.next();
508 var res = FileAPI.parseJSON(res.responseText);
509 equal(res.images['image'].width, 100, 'max.width');
510 equal(res.images['image'].height, 71, 'max.height');
511 }
512 });
513
514 // strategy: 'height'
515 queue.inc();
516 FileAPI.upload({
517 url: controllerUrl,
518 files: { image: file },
519 imageTransform: { width: 100, height: 100, strategy: 'height' },
520 complete: function (err, res){
521 queue.next();
522 var res = FileAPI.parseJSON(res.responseText);
523 equal(res.images['image'].width, 141, 'height.width');
524 equal(res.images['image'].height, 100, 'height.height');
525 }
526 });
527
528 // strategy: 'width'
529 queue.inc();
530 FileAPI.upload({
531 url: controllerUrl,
532 files: { image: file },
533 imageTransform: { width: 100, height: 100, strategy: 'width' },
534 complete: function (err, res){
535 queue.next();
536 var res = FileAPI.parseJSON(res.responseText);
537 equal(res.images['image'].width, 100, 'width.width');
538 equal(res.images['image'].height, 70, 'width.height');
539 }
540 });
541
542 // preview
543 queue.inc();
544 FileAPI.upload({
545 url: controllerUrl,
546 files: { image: file },
547 imageTransform: { width: 100, height: 100, rotate: 'auto', preview: true },
548 complete: function (err, res){
549 var res = FileAPI.parseJSON(res.responseText);
550
551 imageEqual(res.images.image.dataURL, 'files/samples/'+browser+'-image-auto-100x100.jpeg', 'image auto 100x100.png', function (){
552 queue.next();
553 });
554 }
555 });
556 });
557
558 test('upload + autoOrientation', function (){
559 var file = FileAPI.getFiles(uploadForm['image.jpg'])[0];
560 var queue = FileAPI.queue(start);
561 var check = function (err, res){
562 var res = FileAPI.parseJSON(res.responseText);
563 equal(res.images.image.width, 448, this+'.width');
564 equal(res.images.image.height, 632, this+'.height');
565 queue.next();
566 };
567
568 stop();
569
570 queue.inc();
571 FileAPI.upload({
572 url: controllerUrl,
573 files: { image: file },
574 imageAutoOrientation: true,
575 complete: check.bind('imageAutoOrientation')
576 });
577
578 queue.inc();
579 FileAPI.upload({
580 url: controllerUrl,
581 files: { image: file },
582 imageTransform: { rotate: 'auto' },
583 complete: check.bind('imageTransform.rotate.auto')
584 });
585
586 queue.inc();
587 FileAPI.upload({
588 url: controllerUrl,
589 files: { image: FileAPI.Image(file).rotate('auto') },
590 complete: check.bind('FileAPI.Image.fn.rotate.auto')
591 });
592 });
593
594 FileAPI.html5 && test('upload + CamanJS', function (){
595 stop();
596 FileAPI.Image(FileAPI.getFiles(uploadForm['dino.png'])[0])
597 .preview(50, 30)
598 .filter('vintage')
599 .get(function (err, canvas){
600 equal(canvas.nodeName.toLowerCase(), 'canvas');
601
602 FileAPI.upload({
603 url: controllerUrl,
604 files: {
605 image: {
606 name: 'my-file',
607 blob: canvas
608 }
609 },
610 complete: function (err, xhr){
611 var res = FileAPI.parseJSON(xhr.responseText);
612 imageEqual(res.images['image'].dataURL, 'files/samples/'+browser+'-vintage.png', 'caman vintage', function (){
613 start();
614 }, .9);
615 }
616 })
617 })
618 ;
619 });
620
621 0 && FileAPI.html5 && test('upload + multi imageTransform', function (){
622 var file = FileAPI.getFiles(uploadForm['dino.png'])[0];
623
624 stop();
625 FileAPI.upload({
626 url: controllerUrl,
627 files: { image: file },
628 imageTransform: {
629 'jpeg': {
630 width: 50,
631 height: 50,
632 type: 'image/jpeg'
633 },
634 '180deg': {
635 width: 50,
636 height: 50,
637 rotate: 180
638 },
639 'custom': function (img, trans){
640 trans.crop(100, 100, 200, 200).resize(20, 20);
641 }
642 },
643 complete: function (err, res){
644 var res = FileAPI.parseJSON(res.responseText);
645
646 imageEqual(res.images['jpeg'].dataURL, 'files/samples/'+browser+'-dino-50x50.jpeg', 'dino jpeg', function (){
647 imageEqual(res.images['180deg'].dataURL, 'files/samples/'+browser+'-dino-180deg-50x50.png', 'dino 180 deg', function (){
648 imageEqual(res.images['custom'].dataURL, 'files/samples/'+browser+'-dino-custom.png', 'dino custom', function (){
649 start();
650 });
651 });
652 });
653 }
654 });
655 });
656
657 FileAPI.html5 && test('upload + imageTransform with postName', function (){
658 var file = FileAPI.getFiles(uploadForm['dino.png'])[0];
659
660 stop();
661 FileAPI.upload({
662 url: controllerUrl,
663 files: { image: file },
664 imageTransform: {
665 '180deg': {
666 postName: '180deg',
667 width: 50,
668 height: 50,
669 rotate: 180
670 }
671 },
672 complete: function (err, res){
673 var res = FileAPI.parseJSON(res.responseText);
674 ok('image' in res.data._FILES);
675 ok('180deg' in res.data._FILES);
676 equal(res.data._FILES['image'].name, 'dino.png');
677 equal(res.data._FILES['180deg'].name, 'dino.png');
678 start();
679 }
680 });
681 });
682
683 0 && test('iframe', function (){
684 var html5 = FileAPI.support.html5;
685 var queue = FileAPI.queue(function (){
686 start();
687 FileAPI.support.html5 = html5;
688 });
689
690 stop();
691 FileAPI.support.html5 = false;
692
693 // default callback
694 queue.inc();
695 FileAPI.upload({
696 url: controllerUrl,
697 complete: function (err, xhr){
698 var json = FileAPI.parseJSON(xhr.responseText);
699 equal(json.jsonp, 'callback', 'default');
700 queue.next();
701 }
702 });
703
704 // callback in GET
705 queue.inc();
706 FileAPI.upload({
707 url: 'http://rubaxa.org/FileAPI/server/ctrl.php?fn=?',
708 complete: function (err, xhr){
709 var json = FileAPI.parseJSON(xhr.responseText);
710 equal(json.jsonp, 'fn', 'custom');
711 queue.next();
712 }
713 });
714
715 // 302: redirect
716 queue.inc();
717 FileAPI.upload({
718 url: 'http://rubaxa.org/FileAPI/server/redirect.php?page=json.html',
719 complete: function (err, xhr){
720 equal(xhr.responseText, 'done', '302');
721 queue.next();
722 }
723 });
724 });
725
726 FileAPI.html5 && test('WebCam', function (){
727 stop();
728 FileAPI.Camera.publish(document.getElementById('web-cam'), function (err, cam){
729 if( err ){
730 ok(browser, 'phantomjs');
731 start();
732 }
733 else {
734 var shot = cam.shot();
735
736 FileAPI.upload({
737 url: controllerUrl,
738 files: { shot: shot },
739 complete: function (err, res){
740 var res = FileAPI.parseJSON(res.responseText);
741
742 imageEqual(res.images.shot.dataURL, shot, 'shot', function (){
743 start();
744 });
745 }
746 });
747 }
748 });
749 });
750
751})();