2 * jQuery File Upload Plugin Test 6.6
3 * https://github.com/blueimp/jQuery-File-Upload
5 * Copyright 2010, Sebastian Tschan
8 * Licensed under the MIT license:
9 * http://www.opensource.org/licenses/MIT
12 /*jslint nomen: true, unparam: true */
13 /*global $, QUnit, document, expect, module, test, asyncTest, start, ok, strictEqual, notStrictEqual */
18 QUnit.done = function () {
19 // Delete all uploaded files:
20 var url = $('#fileupload').find('form').prop('action');
21 $.getJSON(url, function (files) {
22 $.each(files, function (index, file) {
24 url: url + '?file=' + encodeURIComponent(file.name),
33 // Set the .fileupload method to the basic widget method:
34 $.widget('blueimp.fileupload', $.blueimp.fileupload, {});
36 teardown: function () {
37 // De-initialize the file input plugin:
38 $('#fileupload:blueimp-fileupload').fileupload('destroy');
39 // Remove all remaining event listeners:
40 $('#fileupload input').unbind();
46 // Set the .fileupload method to the UI widget method:
47 $.widget('blueimpUI.fileupload', $.blueimpUI.fileupload, {});
49 teardown: function () {
50 // De-initialize the file input plugin:
51 $('#fileupload:blueimpUI-fileupload').fileupload('destroy');
52 // Remove all remaining event listeners:
53 $('#fileupload input, #fileupload button').unbind();
58 module('Initialization', lifecycle);
60 test('Widget initialization', function () {
61 ok($('#fileupload').fileupload().data('fileupload'));
64 test('Data attribute options', function () {
65 $('#fileupload').attr('data-url', 'http://example.org');
66 $('#fileupload').fileupload();
68 $('#fileupload').fileupload('option', 'url'),
73 test('File input initialization', function () {
74 var fu = $('#fileupload').fileupload();
76 fu.fileupload('option', 'fileInput').length,
77 'File input field inside of the widget'
80 fu.fileupload('option', 'fileInput').length,
81 'Widget element as file input field'
85 test('Drop zone initialization', function () {
86 ok($('#fileupload').fileupload()
87 .fileupload('option', 'dropZone').length);
90 test('Event listeners initialization', function () {
91 var fu = $('#fileupload').fileupload();
93 fu.fileupload('option', 'fileInput')
94 .data('events').change.length,
95 'Listens to file input change events'
97 if ($.support.xhrFormDataFileUpload) {
99 fu.fileupload('option', 'dropZone')
100 .data('events').drop.length,
101 'Listens to drop zone drop events'
104 fu.fileupload('option', 'dropZone')
105 .data('events').dragover.length,
106 'Listens to drop zone dragover events'
111 module('API', lifecycle);
113 test('destroy', function () {
114 var fu = $('#fileupload').fileupload(),
115 fileInput = fu.fileupload('option', 'fileInput'),
116 dropZone = fu.fileupload('option', 'dropZone');
117 fileInput.change($.noop);
118 dropZone.bind('drop', $.noop);
119 dropZone.bind('dragover', $.noop);
120 fu.fileupload('destroy');
122 fileInput.data('events').change.length,
124 'Removes own file input change event listener'
126 if ($.support.xhrFormDataFileUpload) {
128 dropZone.data('events').drop.length,
130 'Removes own drop zone drop event listener'
133 dropZone.data('events').dragover.length,
135 'Removes own drop zone dragover event listener'
140 test('disable', function () {
141 var fu = $('#fileupload').fileupload(),
142 fileInput = fu.fileupload('option', 'fileInput'),
143 dropZone = fu.fileupload('option', 'dropZone'),
144 param = {files: [{name: 'test'}]};
145 fileInput.change($.noop);
146 dropZone.bind('drop', $.noop);
147 dropZone.bind('dragover', $.noop);
148 fu.fileupload('disable');
150 fileInput.data('events').change.length,
152 'Removes own file input change event listener'
154 if ($.support.xhrFormDataFileUpload) {
156 dropZone.data('events').drop.length,
158 'Removes own drop zone drop event listener'
161 dropZone.data('events').dragover.length,
163 'Removes own drop zone dragover event listener'
167 add: function (e, data) {
170 }).fileupload('add', param);
173 test('enable', function () {
174 var fu = $('#fileupload').fileupload(),
175 param = {files: [{name: 'test'}]};
176 fu.fileupload('disable');
177 fu.fileupload('enable');
179 fu.fileupload('option', 'fileInput')
180 .data('events').change.length,
181 'Listens to file input change events'
183 if ($.support.xhrFormDataFileUpload) {
185 fu.fileupload('option', 'dropZone')
186 .data('events').drop.length,
187 'Listens to drop zone drop events'
190 fu.fileupload('option', 'dropZone')
191 .data('events').dragover.length,
192 'Listens to drop zone dragover events'
195 $('#fileupload').fileupload({
196 send: function (e, data) {
200 'Triggers send callback'
204 }).fileupload('send', param);
207 test('option', function () {
208 var fu = $('#fileupload').fileupload(),
209 fileInput = fu.fileupload('option', 'fileInput'),
210 dropZone = fu.fileupload('option', 'dropZone');
211 fu.fileupload('option', 'fileInput', null);
212 fu.fileupload('option', 'dropZone', null);
214 !fileInput.data('events'),
215 'Removes event listener after changing fileInput option'
217 if ($.support.xhrFormDataFileUpload) {
219 !dropZone.data('events'),
220 'Removes event listeners after changing dropZone option'
223 fu.fileupload('option', 'fileInput', fileInput);
224 fu.fileupload('option', 'dropZone', dropZone);
226 fileInput.data('events').change.length,
227 'Adds change event listener after setting fileInput option'
229 if ($.support.xhrFormDataFileUpload) {
231 dropZone.data('events').drop.length,
232 'Adds drop event listener after setting dropZone option'
235 dropZone.data('events').dragover.length,
236 'Adds dragover event listener after setting dropZone option'
239 fu.fileupload('option', 'dropZone', 'body');
241 fu.fileupload('option', 'dropZone')[0],
243 'Allow a query string as parameter for the dropZone option'
245 fu.fileupload('option', 'dropZone', document);
247 fu.fileupload('option', 'dropZone')[0],
249 'Allow a document element as parameter for the dropZone option'
251 fu.fileupload('option', 'fileInput', ':file');
253 fu.fileupload('option', 'fileInput')[0],
255 'Allow a query string as parameter for the fileInput option'
257 fu.fileupload('option', 'fileInput', $(':file')[0]);
259 fu.fileupload('option', 'fileInput')[0],
261 'Allow a document element as parameter for the fileInput option'
265 asyncTest('add', function () {
267 var param = {files: [{name: 'test'}]},
268 param2 = {files: [{fileName: 'test', fileSize: 123}]};
269 $('#fileupload').fileupload({
270 add: function (e, data) {
274 'Triggers add callback'
277 }).fileupload('add', param).fileupload(
283 param2.files[0].fileName,
284 'Normalizes fileName'
288 param2.files[0].fileSize,
289 'Normalizes fileSize'
291 data.submit().complete(function () {
292 ok(true, 'data.submit() Returns a jqXHR object');
296 ).fileupload('add', param2);
299 asyncTest('send', function () {
301 var param = {files: [{name: 'test'}]};
302 $('#fileupload').fileupload({
303 send: function (e, data) {
307 'Triggers send callback'
310 }).fileupload('send', param).fail(function () {
311 ok(true, 'Allows to abort the request');
312 }).complete(function () {
313 ok(true, 'Returns a jqXHR object');
318 module('Callbacks', lifecycle);
320 asyncTest('add', function () {
322 var param = {files: [{name: 'test'}]};
323 $('#fileupload').fileupload({
324 add: function (e, data) {
325 ok(true, 'Triggers add callback');
328 }).fileupload('add', param);
331 asyncTest('submit', function () {
333 var param = {files: [{name: 'test'}]};
334 $('#fileupload').fileupload({
335 submit: function (e, data) {
336 ok(true, 'Triggers submit callback');
340 }).fileupload('add', param);
343 asyncTest('send', function () {
345 var param = {files: [{name: 'test'}]};
346 $('#fileupload').fileupload({
347 send: function (e, data) {
348 ok(true, 'Triggers send callback');
352 }).fileupload('send', param);
355 asyncTest('done', function () {
357 var param = {files: [{name: 'test'}]};
358 $('#fileupload').fileupload({
359 done: function (e, data) {
360 ok(true, 'Triggers done callback');
363 }).fileupload('send', param);
366 asyncTest('fail', function () {
368 var param = {files: [{name: 'test'}]},
369 fu = $('#fileupload').fileupload({
371 fail: function (e, data) {
372 ok(true, 'Triggers fail callback');
376 fu.data('fileupload')._isXHRUpload = function () {
379 fu.fileupload('send', param);
382 asyncTest('always', function () {
384 var param = {files: [{name: 'test'}]},
386 fu = $('#fileupload').fileupload({
387 always: function (e, data) {
388 ok(true, 'Triggers always callback');
396 fu.data('fileupload')._isXHRUpload = function () {
399 fu.fileupload('add', param).fileupload(
403 ).fileupload('add', param);
406 asyncTest('progress', function () {
408 var param = {files: [{name: 'test'}]},
410 $('#fileupload').fileupload({
411 forceIframeTransport: true,
412 progress: function (e, data) {
413 ok(true, 'Triggers progress callback');
420 }).fileupload('send', param);
423 asyncTest('progressall', function () {
425 var param = {files: [{name: 'test'}]},
427 $('#fileupload').fileupload({
428 forceIframeTransport: true,
429 progressall: function (e, data) {
430 ok(true, 'Triggers progressall callback');
437 }).fileupload('send', param);
440 asyncTest('start', function () {
442 var param = {files: [{name: '1'}, {name: '2'}]},
444 $('#fileupload').fileupload({
445 send: function (e, data) {
448 start: function (e, data) {
449 ok(!active, 'Triggers start callback before uploads');
452 }).fileupload('send', param);
455 asyncTest('stop', function () {
457 var param = {files: [{name: '1'}, {name: '2'}]},
459 $('#fileupload').fileupload({
460 send: function (e, data) {
463 always: function (e, data) {
466 stop: function (e, data) {
467 ok(!active, 'Triggers stop callback after uploads');
470 }).fileupload('send', param);
473 test('change', function () {
474 var fu = $('#fileupload').fileupload(),
475 fuo = fu.data('fileupload'),
476 fileInput = fu.fileupload('option', 'fileInput');
479 change: function (e, data) {
480 ok(true, 'Triggers change callback');
484 'Creates pseudo File object'
490 data: {fileupload: fuo},
495 test('paste', function () {
496 var fu = $('#fileupload').fileupload(),
497 fuo = fu.data('fileupload');
500 paste: function (e, data) {
501 ok(true, 'Triggers paste callback');
506 data: {fileupload: fuo},
507 originalEvent: {clipboardData: {}},
508 preventDefault: $.noop
512 test('drop', function () {
513 var fu = $('#fileupload').fileupload(),
514 fuo = fu.data('fileupload');
517 drop: function (e, data) {
518 ok(true, 'Triggers drop callback');
523 data: {fileupload: fuo},
524 originalEvent: {dataTransfer: {}},
525 preventDefault: $.noop
529 test('dragover', function () {
530 var fu = $('#fileupload').fileupload(),
531 fuo = fu.data('fileupload');
534 dragover: function (e, data) {
535 ok(true, 'Triggers dragover callback');
540 data: {fileupload: fuo},
541 originalEvent: {dataTransfer: {}},
542 preventDefault: $.noop
546 module('Options', lifecycle);
548 test('paramName', function () {
550 var param = {files: [{name: 'test'}]};
551 $('#fileupload').fileupload({
553 send: function (e, data) {
556 data.fileInput.prop('name'),
557 'Takes paramName from file input field if not set'
561 }).fileupload('send', param);
564 test('url', function () {
566 var param = {files: [{name: 'test'}]};
567 $('#fileupload').fileupload({
569 send: function (e, data) {
572 $(data.fileInput.prop('form')).prop('action'),
573 'Takes url from form action if not set'
577 }).fileupload('send', param);
580 test('type', function () {
582 var param = {files: [{name: 'test'}]};
583 $('#fileupload').fileupload({
585 send: function (e, data) {
589 'Request type is "POST" if not set to "PUT"'
593 }).fileupload('send', param);
594 $('#fileupload').fileupload({
596 send: function (e, data) {
600 'Request type is "PUT" if set to "PUT"'
604 }).fileupload('send', param);
607 test('replaceFileInput', function () {
608 var fu = $('#fileupload').fileupload(),
609 fuo = fu.data('fileupload'),
610 fileInput = fu.fileupload('option', 'fileInput'),
611 fileInputElement = fileInput[0];
614 replaceFileInput: false,
615 change: function (e, data) {
617 fu.fileupload('option', 'fileInput')[0],
619 'Keeps file input with replaceFileInput: false'
625 data: {fileupload: fuo},
629 replaceFileInput: true,
630 change: function (e, data) {
632 fu.fileupload('option', 'fileInput')[0],
634 'Replaces file input with replaceFileInput: true'
640 data: {fileupload: fuo},
645 asyncTest('forceIframeTransport', function () {
647 var param = {files: [{name: 'test'}]};
648 $('#fileupload').fileupload({
649 forceIframeTransport: true,
650 done: function (e, data) {
652 data.dataType.substr(0, 6),
654 'Iframe Transport is used'
658 }).fileupload('send', param);
661 test('singleFileUploads', function () {
663 var fu = $('#fileupload').fileupload(),
664 param = {files: [{name: '1'}, {name: '2'}]},
666 fu.data('fileupload')._isXHRUpload = function () {
669 $('#fileupload').fileupload({
670 singleFileUploads: true,
671 add: function (e, data) {
672 ok(true, 'Triggers callback number ' + index.toString());
675 }).fileupload('add', param).fileupload(
679 ).fileupload('add', param);
682 test('limitMultiFileUploads', function () {
684 var fu = $('#fileupload').fileupload(),
693 fu.data('fileupload')._isXHRUpload = function () {
696 $('#fileupload').fileupload({
697 singleFileUploads: false,
698 limitMultiFileUploads: 2,
699 add: function (e, data) {
700 ok(true, 'Triggers callback number ' + index.toString());
703 }).fileupload('add', param);
706 asyncTest('sequentialUploads', function () {
708 var param = {files: [
719 fu = $('#fileupload').fileupload({
720 sequentialUploads: true,
721 add: function (e, data) {
723 if (addIndex === 4) {
724 data.submit().abort();
729 send: function (e, data) {
732 done: function (e, data) {
734 strictEqual(sendIndex, loadIndex, 'upload in order');
736 fail: function (e, data) {
737 strictEqual(data.errorThrown, 'abort', 'upload aborted');
743 fu.data('fileupload')._isXHRUpload = function () {
746 fu.fileupload('add', param);
749 asyncTest('limitConcurrentUploads', function () {
751 var param = {files: [
768 fu = $('#fileupload').fileupload({
769 limitConcurrentUploads: 3,
770 add: function (e, data) {
772 if (addIndex === 4) {
773 data.submit().abort();
778 send: function (e, data) {
781 done: function (e, data) {
783 ok(sendIndex - loadIndex < 3);
785 fail: function (e, data) {
786 strictEqual(data.errorThrown, 'abort', 'upload aborted');
792 fu.data('fileupload')._isXHRUpload = function () {
795 fu.fileupload('add', param);
798 if ($.support.xhrFileUpload) {
799 asyncTest('multipart', function () {
801 var param = {files: [{
806 fu = $('#fileupload').fileupload({
808 always: function (e, data) {
812 'non-multipart upload sets file type as contentType'
815 data.headers['X-File-Name'],
817 'non-multipart upload sets X-File-Name header'
820 data.headers['X-File-Type'],
822 'non-multipart upload sets X-File-Type header'
825 data.headers['X-File-Size'],
827 'non-multipart upload sets X-File-Size header'
832 fu.fileupload('send', param);
836 module('UI Initialization', lifecycleUI);
838 test('Widget initialization', function () {
839 ok($('#fileupload').fileupload().data('fileupload'));
841 $('#fileupload').fileupload('option', 'uploadTemplate').length,
842 'Initialized upload template'
845 $('#fileupload').fileupload('option', 'downloadTemplate').length,
846 'Initialized download template'
850 test('Buttonbar event listeners', function () {
851 var buttonbar = $('#fileupload .fileupload-buttonbar'),
852 files = [{name: 'test'}];
854 $('#fileupload').fileupload({
855 send: function (e, data) {
856 ok(true, 'Started file upload via global start button');
858 fail: function (e, data) {
859 ok(true, 'Canceled file upload via global cancel button');
860 data.context.remove();
862 destroy: function (e, data) {
863 ok(true, 'Delete action called via global delete button');
867 buttonbar.find('.start')
868 .data('events').click.length,
869 'Listens to start button click events'
872 buttonbar.find('.cancel')
873 .data('events').click.length,
874 'Listens to cancel button click events'
877 buttonbar.find('.delete')
878 .data('events').click.length,
879 'Listens to delete button click events'
881 $('#fileupload').fileupload('add', {files: files});
882 buttonbar.find('.cancel').click();
883 $('#fileupload').fileupload('add', {files: files});
884 buttonbar.find('.start').click();
885 buttonbar.find('.cancel').click();
886 $('#fileupload').data('fileupload')._renderDownload(files)
887 .appendTo($('#fileupload .files')).show()
888 .find('.delete input').click();
889 buttonbar.find('.delete').click();
892 module('UI API', lifecycleUI);
894 test('destroy', function () {
895 var buttonbar = $('#fileupload .fileupload-buttonbar');
896 $('#fileupload').fileupload();
897 buttonbar.find('button').click($.noop);
898 $('#fileupload').fileupload('destroy');
900 buttonbar.find('.start').data('events').click.length,
902 'Removes own start button click event listener'
905 buttonbar.find('.cancel').data('events').click.length,
907 'Removes own cancel button click event listener'
910 buttonbar.find('.delete').data('events').click.length,
912 'Removes own delete button click event listener'
916 test('disable', function () {
917 var buttonbar = $('#fileupload .fileupload-buttonbar');
918 $('#fileupload').fileupload();
919 $('#fileupload').fileupload('disable');
921 buttonbar.find('input[type=file], button').not(':disabled').length,
923 'Disables the buttonbar buttons'
927 test('enable', function () {
928 var buttonbar = $('#fileupload .fileupload-buttonbar');
931 .fileupload('disable')
932 .fileupload('enable');
934 buttonbar.find('input[type=file], button').not(':disabled').length,
936 'Enables the buttonbar buttons'
940 module('UI Callbacks', lifecycleUI);
942 test('destroy', function () {
944 $('#fileupload').fileupload({
945 destroy: function (e, data) {
946 ok(true, 'Triggers destroy callback');
950 'Passes over deletion url parameter'
955 'Passes over deletion request type parameter'
959 $('#fileupload').data('fileupload')._renderDownload([{
962 delete_type: 'DELETE'
963 }]).appendTo($('#fileupload .files')).show()
964 .find('.delete input').click();
965 $('#fileupload .fileupload-buttonbar .delete').click();
968 asyncTest('added', function () {
970 var param = {files: [{name: 'test'}]};
971 $('#fileupload').fileupload({
972 added: function (e, data) {
977 'Triggers added callback'
983 }).fileupload('add', param);
986 asyncTest('started', function () {
988 var param = {files: [{name: 'test'}]};
989 $('#fileupload').fileupload({
990 started: function (e) {
992 ok('Triggers started callback');
995 sent: function (e, data) {
998 }).fileupload('send', param);
1001 asyncTest('sent', function () {
1003 var param = {files: [{name: 'test'}]};
1004 $('#fileupload').fileupload({
1005 sent: function (e, data) {
1009 param.files[0].name,
1010 'Triggers sent callback'
1014 }).fileupload('send', param);
1017 asyncTest('completed', function () {
1019 var param = {files: [{name: 'test'}]};
1020 $('#fileupload').fileupload({
1021 completed: function (e, data) {
1023 ok('Triggers completed callback');
1026 }).fileupload('send', param);
1029 asyncTest('failed', function () {
1031 var param = {files: [{name: 'test'}]};
1032 $('#fileupload').fileupload({
1033 failed: function (e, data) {
1035 ok('Triggers failed callback');
1038 }).fileupload('send', param).abort();
1041 asyncTest('stopped', function () {
1043 var param = {files: [{name: 'test'}]};
1044 $('#fileupload').fileupload({
1045 stopped: function (e, data) {
1047 ok('Triggers stopped callback');
1050 }).fileupload('send', param);
1053 asyncTest('destroyed', function () {
1055 $('#fileupload').fileupload({
1056 destroyed: function (e, data) {
1058 ok(true, 'Triggers destroyed callback');
1061 $('#fileupload').data('fileupload')._renderDownload([{
1064 delete_type: 'DELETE'
1065 }]).appendTo($('#fileupload .files')).show()
1066 .find('.delete input').click();
1067 $('#fileupload .fileupload-buttonbar .delete').click();
1070 module('UI Options', lifecycleUI);
1072 test('autoUpload', function () {
1077 send: function (e, data) {
1078 ok(true, 'Started file upload automatically');
1082 .fileupload('add', {files: [{name: 'test'}]})
1083 .fileupload('option', 'autoUpload', false)
1084 .fileupload('add', {files: [{name: 'test'}]});
1087 test('maxNumberOfFiles', function () {
1094 maxNumberOfFiles: 1,
1095 singleFileUploads: false,
1096 send: function (e, data) {
1105 .fileupload('add', {files: [{name: (addIndex += 1)}]})
1106 .fileupload('add', {files: [{name: 'test'}]})
1107 .fileupload('option', 'maxNumberOfFiles', 1)
1108 .fileupload('add', {files: [{name: 1}, {name: 2}]})
1110 maxNumberOfFiles: 1,
1111 send: function (e, data) {
1119 .fileupload('add', {files: [{name: (addIndex += 1)}]})
1120 .fileupload('add', {files: [{name: (addIndex += 1)}]})
1122 maxNumberOfFiles: 0,
1123 send: function (e, data) {
1125 !$.blueimpUI.fileupload.prototype.options
1126 .send.call(this, e, data)
1131 .fileupload('send', {files: [{name: 'test'}]});
1134 test('maxFileSize', function () {
1142 send: function (e, data) {
1150 .fileupload('add', {files: [{
1151 name: (addIndex += 1)
1153 .fileupload('add', {files: [{
1154 name: (addIndex += 1),
1157 .fileupload('add', {files: [{
1162 send: function (e, data) {
1164 !$.blueimpUI.fileupload.prototype.options
1165 .send.call(this, e, data)
1170 .fileupload('send', {files: [{
1176 test('minFileSize', function () {
1184 send: function (e, data) {
1192 .fileupload('add', {files: [{
1193 name: (addIndex += 1)
1195 .fileupload('add', {files: [{
1196 name: (addIndex += 1),
1199 .fileupload('add', {files: [{
1204 send: function (e, data) {
1206 !$.blueimpUI.fileupload.prototype.options
1207 .send.call(this, e, data)
1212 .fileupload('send', {files: [{
1218 test('acceptFileTypes', function () {
1225 acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
1226 previewFileTypes: /none/,
1227 send: function (e, data) {
1235 .fileupload('add', {files: [{
1236 name: (addIndex += 1) + '.jpg'
1238 .fileupload('add', {files: [{
1239 name: (addIndex += 1),
1242 .fileupload('add', {files: [{
1247 send: function (e, data) {
1249 !$.blueimpUI.fileupload.prototype.options
1250 .send.call(this, e, data)
1255 .fileupload('send', {files: [{