fileUploadSpec.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /**
  2. * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
  3. *
  4. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  5. * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
  6. * @author Julius Härtl <jus@bitgrid.net>
  7. * @author Morris Jobke <hey@morrisjobke.de>
  8. * @author Tomasz Grobelny <tomasz@grobelny.net>
  9. * @author Vincent Petry <vincent@nextcloud.com>
  10. *
  11. * @license AGPL-3.0-or-later
  12. *
  13. * This program is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU Affero General Public License as
  15. * published by the Free Software Foundation, either version 3 of the
  16. * License, or (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU Affero General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Affero General Public License
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. *
  26. */
  27. describe('OC.Upload tests', function() {
  28. var $dummyUploader;
  29. var testFile;
  30. var uploader;
  31. var failStub;
  32. var progressBarStub;
  33. beforeEach(function() {
  34. testFile = {
  35. name: 'test.txt',
  36. size: 5000, // 5 KB
  37. type: 'text/plain',
  38. lastModifiedDate: new Date()
  39. };
  40. // need a dummy button because file-upload checks on it
  41. $('#testArea').append(
  42. '<input type="file" id="file_upload_start" name="files[]" multiple="multiple">' +
  43. '<input type="hidden" id="free_space" name="free_space" value="50000000">' + // 50 MB
  44. // TODO: handlebars!
  45. '<div id="new">' +
  46. '<a>New</a>' +
  47. '<ul>' +
  48. '<li data-type="file" data-newname="New text file.txt"><p>Text file</p></li>' +
  49. '</ul>' +
  50. '</div>'
  51. );
  52. $dummyUploader = $('#file_upload_start');
  53. progressBarStub = {on: function(){}};
  54. uploader = new OC.Uploader($dummyUploader, {progressBar: progressBarStub});
  55. failStub = sinon.stub();
  56. uploader.on('fail', failStub);
  57. });
  58. afterEach(function() {
  59. $dummyUploader = undefined;
  60. failStub = undefined;
  61. });
  62. /**
  63. * Add file for upload
  64. * @param {Array.<File>} files array of file data to simulate upload
  65. * @return {Array.<Object>} array of uploadinfo or null if add() returned false
  66. */
  67. function addFiles(uploader, files) {
  68. return _.map(files, function(file) {
  69. var jqXHR = {status: 200};
  70. var uploadInfo = {
  71. originalFiles: files,
  72. files: [file],
  73. jqXHR: jqXHR,
  74. response: sinon.stub().returns(jqXHR),
  75. targetDir: "/",
  76. submit: sinon.stub(),
  77. abort: sinon.stub()
  78. };
  79. if (uploader.fileUploadParam.add.call(
  80. $dummyUploader[0],
  81. {},
  82. uploadInfo
  83. )) {
  84. return uploadInfo;
  85. }
  86. return null;
  87. });
  88. }
  89. describe('Adding files for upload', function() {
  90. it('adds file when size is below limits', function(done) {
  91. var result = addFiles(uploader, [testFile]);
  92. expect(result[0]).not.toEqual(null);
  93. result[0].submit.callsFake(function(){
  94. expect(result[0].submit.calledOnce).toEqual(true);
  95. done();
  96. });
  97. });
  98. it('adds file when free space is unknown', function(done) {
  99. var result;
  100. $('#free_space').val(-2);
  101. result = addFiles(uploader, [testFile]);
  102. expect(result[0]).not.toEqual(null);
  103. result[0].submit.callsFake(function(){
  104. expect(result[0].submit.calledOnce).toEqual(true);
  105. expect(failStub.notCalled).toEqual(true);
  106. done();
  107. });
  108. });
  109. it('does not add file if it exceeds free space', function(done) {
  110. var result;
  111. $('#free_space').val(1000);
  112. failStub.callsFake(function(){
  113. expect(failStub.calledOnce).toEqual(true);
  114. expect(failStub.getCall(0).args[1].textStatus).toEqual('notenoughspace');
  115. expect(failStub.getCall(0).args[1].errorThrown).toEqual(
  116. 'Not enough free space, you are uploading 5 KB but only 1000 B is left'
  117. );
  118. setTimeout(done, 0);
  119. });
  120. result = addFiles(uploader, [testFile]);
  121. expect(result[0]).toEqual(null);
  122. });
  123. });
  124. describe('Upload conflicts', function() {
  125. var conflictDialogStub;
  126. var clock;
  127. var fileList;
  128. beforeEach(function() {
  129. $('#testArea').append(
  130. '<div id="tableContainer">' +
  131. '<table class="files-filestable list-container view-grid">' +
  132. '<thead><tr>' +
  133. '<th class="hidden column-name">' +
  134. '<input type="checkbox" id="select_all_files" class="select-all">' +
  135. '<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
  136. '<span class="selectedActions hidden">' +
  137. '<a href class="download"><img src="actions/download.svg">Download</a>' +
  138. '<a href class="delete-selected">Delete</a></span>' +
  139. '</th>' +
  140. '<th class="hidden column-size"><a class="columntitle" data-sort="size"><span class="sort-indicator"></span></a></th>' +
  141. '<th class="hidden column-mtime"><a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a></th>' +
  142. '</tr></thead>' +
  143. '<tbody class="files-fileList"></tbody>' +
  144. '<tfoot></tfoot>' +
  145. '</table>' +
  146. '</div>'
  147. );
  148. fileList = new OCA.Files.FileList($('#tableContainer'));
  149. fileList.add({name: 'conflict.txt', mimetype: 'text/plain'});
  150. fileList.add({name: 'conflict2.txt', mimetype: 'text/plain'});
  151. conflictDialogStub = sinon.stub(OC.dialogs, 'fileexists');
  152. uploader = new OC.Uploader($dummyUploader, {
  153. progressBar: progressBarStub,
  154. fileList: fileList
  155. });
  156. var deferred = $.Deferred();
  157. conflictDialogStub.returns(deferred.promise());
  158. deferred.resolve();
  159. });
  160. afterEach(function() {
  161. if (clock) {
  162. clock.restore();
  163. clock = undefined
  164. }
  165. conflictDialogStub.restore();
  166. fileList.destroy();
  167. });
  168. it('does not show conflict dialog when no client side conflict', function(done) {
  169. $('#free_space').val(200000);
  170. var counter = 0;
  171. var fun = function() {
  172. counter++;
  173. if(counter != 2) {
  174. return;
  175. }
  176. expect(result[0].submit.calledOnce).toEqual(true);
  177. expect(result[1].submit.calledOnce).toEqual(true);
  178. setTimeout(done, 0);
  179. };
  180. var result = addFiles(uploader, [{name: 'noconflict.txt'}, {name: 'noconflict2.txt'}]);
  181. result[0].submit.callsFake(fun);
  182. result[1].submit.callsFake(fun);
  183. expect(conflictDialogStub.notCalled).toEqual(true);
  184. });
  185. it('shows conflict dialog when no client side conflict', function(done) {
  186. var counter = 0;
  187. conflictDialogStub.callsFake(function(){
  188. counter++;
  189. if(counter != 3) {
  190. return $.Deferred().resolve().promise();
  191. }
  192. setTimeout(function() {
  193. expect(conflictDialogStub.callCount).toEqual(3);
  194. expect(conflictDialogStub.getCall(1).args[0].getFileName())
  195. .toEqual('conflict.txt');
  196. expect(conflictDialogStub.getCall(1).args[1])
  197. .toEqual({ name: 'conflict.txt', mimetype: 'text/plain', directory: '/' });
  198. expect(conflictDialogStub.getCall(1).args[2]).toEqual({ name: 'conflict.txt' });
  199. // yes, the dialog must be called several times...
  200. expect(conflictDialogStub.getCall(2).args[0].getFileName()).toEqual('conflict2.txt');
  201. expect(conflictDialogStub.getCall(2).args[1])
  202. .toEqual({ name: 'conflict2.txt', mimetype: 'text/plain', directory: '/' });
  203. expect(conflictDialogStub.getCall(2).args[2]).toEqual({ name: 'conflict2.txt' });
  204. expect(result[0].submit.calledOnce).toEqual(false);
  205. expect(result[1].submit.calledOnce).toEqual(false);
  206. expect(result[2].submit.calledOnce).toEqual(true);
  207. done();
  208. }, 10);
  209. });
  210. var result = addFiles(uploader, [
  211. {name: 'conflict.txt'},
  212. {name: 'conflict2.txt'},
  213. {name: 'noconflict.txt'}
  214. ]);
  215. });
  216. it('cancels upload when skipping file in conflict mode', function(done) {
  217. var fileData = {name: 'conflict.txt'};
  218. var uploadData = addFiles(uploader, [
  219. fileData
  220. ]);
  221. var upload = new OC.FileUpload(uploader, uploadData[0]);
  222. var deleteStub = sinon.stub(upload, 'deleteUpload');
  223. deleteStub.callsFake(function(){
  224. expect(deleteStub.calledOnce).toEqual(true);
  225. done();
  226. });
  227. uploader.onSkip(upload);
  228. });
  229. it('overwrites file when choosing replace in conflict mode', function(done) {
  230. var fileData = {name: 'conflict.txt'};
  231. var uploadData = addFiles(uploader, [
  232. fileData
  233. ]);
  234. expect(uploadData[0].submit.notCalled).toEqual(true);
  235. var upload = new OC.FileUpload(uploader, uploadData[0]);
  236. uploadData[0].submit.callsFake(function(){
  237. expect(upload.getConflictMode()).toEqual(OC.FileUpload.CONFLICT_MODE_OVERWRITE);
  238. expect(uploadData[0].submit.callCount).toEqual(1);
  239. done();
  240. });
  241. uploader.onReplace(upload);
  242. });
  243. it('autorenames file when choosing replace in conflict mode', function(done) {
  244. var fileData = {name: 'conflict.txt'};
  245. var uploadData = addFiles(uploader, [
  246. fileData
  247. ]);
  248. expect(uploadData[0].submit.notCalled).toEqual(true);
  249. var upload = new OC.FileUpload(uploader, uploadData[0]);
  250. var getResponseStatusStub = sinon.stub(upload, 'getResponseStatus');
  251. var counter = 0;
  252. uploadData[0].submit.callsFake(function(){
  253. counter++;
  254. if(counter===1)
  255. {
  256. expect(upload.getConflictMode()).toEqual(OC.FileUpload.CONFLICT_MODE_AUTORENAME);
  257. expect(upload.getFileName()).toEqual('conflict (2).txt');
  258. expect(uploadData[0].submit.calledOnce).toEqual(true);
  259. getResponseStatusStub.returns(412);
  260. uploader.fileUploadParam.fail.call($dummyUploader[0], {}, uploadData[0]);
  261. }
  262. if(counter===2)
  263. {
  264. _.defer(function() {
  265. expect(upload.getFileName()).toEqual('conflict (3).txt');
  266. expect(uploadData[0].submit.calledTwice).toEqual(true);
  267. done();
  268. })
  269. }
  270. });
  271. uploader.onAutorename(upload);
  272. // in case of server-side conflict, tries to rename again
  273. });
  274. });
  275. });