filelistSpec.js 81 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214
  1. /**
  2. * ownCloud
  3. *
  4. * @author Vincent Petry
  5. * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  9. * License as published by the Free Software Foundation; either
  10. * version 3 of the License, or any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public
  18. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. describe('OCA.Files.FileList tests', function() {
  22. var testFiles, alertStub, notificationStub, fileList, pageSizeStub;
  23. var bcResizeStub;
  24. /**
  25. * Generate test file data
  26. */
  27. function generateFiles(startIndex, endIndex) {
  28. var files = [];
  29. var name;
  30. for (var i = startIndex; i <= endIndex; i++) {
  31. name = 'File with index ';
  32. if (i < 10) {
  33. // do not rely on localeCompare here
  34. // and make the sorting predictable
  35. // cross-browser
  36. name += '0';
  37. }
  38. name += i + '.txt';
  39. files.push({
  40. id: i,
  41. type: 'file',
  42. name: name,
  43. mimetype: 'text/plain',
  44. size: i * 2,
  45. etag: 'abc'
  46. });
  47. }
  48. return files;
  49. }
  50. beforeEach(function() {
  51. alertStub = sinon.stub(OC.dialogs, 'alert');
  52. notificationStub = sinon.stub(OC.Notification, 'show');
  53. // prevent resize algo to mess up breadcrumb order while
  54. // testing
  55. bcResizeStub = sinon.stub(OCA.Files.BreadCrumb.prototype, '_resize');
  56. // init parameters and test table elements
  57. $('#testArea').append(
  58. '<div id="app-content-files">' +
  59. // init horrible parameters
  60. '<input type="hidden" id="dir" value="/subdir"/>' +
  61. '<input type="hidden" id="permissions" value="31"/>' +
  62. // dummy controls
  63. '<div id="controls">' +
  64. ' <div class="actions creatable"></div>' +
  65. ' <div class="notCreatable"></div>' +
  66. '</div>' +
  67. // uploader
  68. '<input type="file" id="file_upload_start" name="files[]" multiple="multiple">' +
  69. // dummy table
  70. // TODO: at some point this will be rendered by the fileList class itself!
  71. '<table id="filestable">' +
  72. '<thead><tr>' +
  73. '<th id="headerName" class="hidden column-name">' +
  74. '<input type="checkbox" id="select_all_files" class="select-all">' +
  75. '<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
  76. '<span class="selectedActions hidden">' +
  77. '<a href class="download">Download</a>' +
  78. '<a href class="delete-selected">Delete</a></span>' +
  79. '</th>' +
  80. '<th class="hidden column-size"><a class="columntitle" data-sort="size"><span class="sort-indicator"></span></a></th>' +
  81. '<th class="hidden column-mtime"><a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a></th>' +
  82. '</tr></thead>' +
  83. '<tbody id="fileList"></tbody>' +
  84. '<tfoot></tfoot>' +
  85. '</table>' +
  86. '<div id="emptycontent">Empty content message</div>' +
  87. '<div class="nofilterresults hidden"></div>' +
  88. '</div>'
  89. );
  90. testFiles = [{
  91. id: 1,
  92. type: 'file',
  93. name: 'One.txt',
  94. mimetype: 'text/plain',
  95. size: 12,
  96. etag: 'abc',
  97. permissions: OC.PERMISSION_ALL
  98. }, {
  99. id: 2,
  100. type: 'file',
  101. name: 'Two.jpg',
  102. mimetype: 'image/jpeg',
  103. size: 12049,
  104. etag: 'def',
  105. permissions: OC.PERMISSION_ALL
  106. }, {
  107. id: 3,
  108. type: 'file',
  109. name: 'Three.pdf',
  110. mimetype: 'application/pdf',
  111. size: 58009,
  112. etag: '123',
  113. permissions: OC.PERMISSION_ALL
  114. }, {
  115. id: 4,
  116. type: 'dir',
  117. name: 'somedir',
  118. mimetype: 'httpd/unix-directory',
  119. size: 250,
  120. etag: '456',
  121. permissions: OC.PERMISSION_ALL
  122. }];
  123. pageSizeStub = sinon.stub(OCA.Files.FileList.prototype, 'pageSize').returns(20);
  124. fileList = new OCA.Files.FileList($('#app-content-files'));
  125. });
  126. afterEach(function() {
  127. testFiles = undefined;
  128. fileList = undefined;
  129. notificationStub.restore();
  130. alertStub.restore();
  131. bcResizeStub.restore();
  132. pageSizeStub.restore();
  133. });
  134. describe('Getters', function() {
  135. it('Returns the current directory', function() {
  136. $('#dir').val('/one/two/three');
  137. expect(fileList.getCurrentDirectory()).toEqual('/one/two/three');
  138. });
  139. it('Returns the directory permissions as int', function() {
  140. $('#permissions').val('23');
  141. expect(fileList.getDirectoryPermissions()).toEqual(23);
  142. });
  143. });
  144. describe('Adding files', function() {
  145. var clock, now;
  146. beforeEach(function() {
  147. // to prevent date comparison issues
  148. clock = sinon.useFakeTimers();
  149. now = new Date();
  150. });
  151. afterEach(function() {
  152. clock.restore();
  153. });
  154. it('generates file element with correct attributes when calling add() with file data', function() {
  155. var fileData = {
  156. id: 18,
  157. type: 'file',
  158. name: 'testName.txt',
  159. mimetype: 'text/plain',
  160. size: '1234',
  161. etag: 'a01234c',
  162. mtime: '123456'
  163. };
  164. var $tr = fileList.add(fileData);
  165. expect($tr).toBeDefined();
  166. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  167. expect($tr.attr('data-id')).toEqual('18');
  168. expect($tr.attr('data-type')).toEqual('file');
  169. expect($tr.attr('data-file')).toEqual('testName.txt');
  170. expect($tr.attr('data-size')).toEqual('1234');
  171. expect($tr.attr('data-etag')).toEqual('a01234c');
  172. expect($tr.attr('data-permissions')).toEqual('31');
  173. expect($tr.attr('data-mime')).toEqual('text/plain');
  174. expect($tr.attr('data-mtime')).toEqual('123456');
  175. expect($tr.find('a.name').attr('href'))
  176. .toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=testName.txt');
  177. expect($tr.find('.nametext').text().trim()).toEqual('testName.txt');
  178. expect($tr.find('.filesize').text()).toEqual('1 kB');
  179. expect($tr.find('.date').text()).not.toEqual('?');
  180. expect(fileList.findFileEl('testName.txt')[0]).toEqual($tr[0]);
  181. });
  182. it('generates dir element with correct attributes when calling add() with dir data', function() {
  183. var fileData = {
  184. id: 19,
  185. type: 'dir',
  186. name: 'testFolder',
  187. mimetype: 'httpd/unix-directory',
  188. size: '1234',
  189. etag: 'a01234c',
  190. mtime: '123456'
  191. };
  192. var $tr = fileList.add(fileData);
  193. expect($tr).toBeDefined();
  194. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  195. expect($tr.attr('data-id')).toEqual('19');
  196. expect($tr.attr('data-type')).toEqual('dir');
  197. expect($tr.attr('data-file')).toEqual('testFolder');
  198. expect($tr.attr('data-size')).toEqual('1234');
  199. expect($tr.attr('data-etag')).toEqual('a01234c');
  200. expect($tr.attr('data-permissions')).toEqual('31');
  201. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  202. expect($tr.attr('data-mtime')).toEqual('123456');
  203. expect($tr.find('.filesize').text()).toEqual('1 kB');
  204. expect($tr.find('.date').text()).not.toEqual('?');
  205. expect(fileList.findFileEl('testFolder')[0]).toEqual($tr[0]);
  206. });
  207. it('generates file element with default attributes when calling add() with minimal data', function() {
  208. var fileData = {
  209. type: 'file',
  210. name: 'testFile.txt'
  211. };
  212. clock.tick(123456);
  213. var $tr = fileList.add(fileData);
  214. expect($tr).toBeDefined();
  215. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  216. expect($tr.attr('data-id')).toBeUndefined();
  217. expect($tr.attr('data-type')).toEqual('file');
  218. expect($tr.attr('data-file')).toEqual('testFile.txt');
  219. expect($tr.attr('data-size')).toBeUndefined();
  220. expect($tr.attr('data-etag')).toBeUndefined();
  221. expect($tr.attr('data-permissions')).toEqual('31');
  222. expect($tr.attr('data-mime')).toBeUndefined();
  223. expect($tr.attr('data-mtime')).toEqual('123456');
  224. expect($tr.find('.filesize').text()).toEqual('Pending');
  225. expect($tr.find('.date').text()).not.toEqual('?');
  226. });
  227. it('generates dir element with default attributes when calling add() with minimal data', function() {
  228. var fileData = {
  229. type: 'dir',
  230. name: 'testFolder'
  231. };
  232. clock.tick(123456);
  233. var $tr = fileList.add(fileData);
  234. expect($tr).toBeDefined();
  235. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  236. expect($tr.attr('data-id')).toBeUndefined();
  237. expect($tr.attr('data-type')).toEqual('dir');
  238. expect($tr.attr('data-file')).toEqual('testFolder');
  239. expect($tr.attr('data-size')).toBeUndefined();
  240. expect($tr.attr('data-etag')).toBeUndefined();
  241. expect($tr.attr('data-permissions')).toEqual('31');
  242. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  243. expect($tr.attr('data-mtime')).toEqual('123456');
  244. expect($tr.find('.filesize').text()).toEqual('Pending');
  245. expect($tr.find('.date').text()).not.toEqual('?');
  246. });
  247. it('generates file element with zero size when size is explicitly zero', function() {
  248. var fileData = {
  249. type: 'dir',
  250. name: 'testFolder',
  251. size: '0'
  252. };
  253. var $tr = fileList.add(fileData);
  254. expect($tr.find('.filesize').text()).toEqual('0 kB');
  255. });
  256. it('generates file element with unknown date when mtime invalid', function() {
  257. var fileData = {
  258. type: 'dir',
  259. name: 'testFolder',
  260. mtime: -1
  261. };
  262. var $tr = fileList.add(fileData);
  263. expect($tr.find('.date .modified').text()).toEqual('?');
  264. });
  265. it('adds new file to the end of the list', function() {
  266. var $tr;
  267. var fileData = {
  268. type: 'file',
  269. name: 'ZZZ.txt'
  270. };
  271. fileList.setFiles(testFiles);
  272. $tr = fileList.add(fileData);
  273. expect($tr.index()).toEqual(4);
  274. });
  275. it('inserts files in a sorted manner when insert option is enabled', function() {
  276. var $tr;
  277. for (var i = 0; i < testFiles.length; i++) {
  278. fileList.add(testFiles[i]);
  279. }
  280. expect(fileList.files[0].name).toEqual('somedir');
  281. expect(fileList.files[1].name).toEqual('One.txt');
  282. expect(fileList.files[2].name).toEqual('Three.pdf');
  283. expect(fileList.files[3].name).toEqual('Two.jpg');
  284. });
  285. it('inserts new file at correct position', function() {
  286. var $tr;
  287. var fileData = {
  288. type: 'file',
  289. name: 'P comes after O.txt'
  290. };
  291. for (var i = 0; i < testFiles.length; i++) {
  292. fileList.add(testFiles[i]);
  293. }
  294. $tr = fileList.add(fileData);
  295. // after "One.txt"
  296. expect($tr.index()).toEqual(2);
  297. expect(fileList.files[2]).toEqual(fileData);
  298. });
  299. it('inserts new folder at correct position in insert mode', function() {
  300. var $tr;
  301. var fileData = {
  302. type: 'dir',
  303. name: 'somedir2 comes after somedir'
  304. };
  305. for (var i = 0; i < testFiles.length; i++) {
  306. fileList.add(testFiles[i]);
  307. }
  308. $tr = fileList.add(fileData);
  309. expect($tr.index()).toEqual(1);
  310. expect(fileList.files[1]).toEqual(fileData);
  311. });
  312. it('inserts new file at the end correctly', function() {
  313. var $tr;
  314. var fileData = {
  315. type: 'file',
  316. name: 'zzz.txt'
  317. };
  318. for (var i = 0; i < testFiles.length; i++) {
  319. fileList.add(testFiles[i]);
  320. }
  321. $tr = fileList.add(fileData);
  322. expect($tr.index()).toEqual(4);
  323. expect(fileList.files[4]).toEqual(fileData);
  324. });
  325. it('removes empty content message and shows summary when adding first file', function() {
  326. var $summary;
  327. var fileData = {
  328. type: 'file',
  329. name: 'first file.txt',
  330. size: 12
  331. };
  332. fileList.setFiles([]);
  333. expect(fileList.isEmpty).toEqual(true);
  334. fileList.add(fileData);
  335. $summary = $('#filestable .summary');
  336. expect($summary.hasClass('hidden')).toEqual(false);
  337. // yes, ugly...
  338. expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
  339. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  340. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  341. expect($summary.find('.filesize').text()).toEqual('12 B');
  342. expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
  343. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  344. expect(fileList.isEmpty).toEqual(false);
  345. });
  346. it('correctly adds the extension markup and show hidden files completely in gray', function() {
  347. var $tr;
  348. var testDataAndExpectedResult = [
  349. {file: {type: 'file', name: 'ZZZ.txt'}, extension: '.txt'},
  350. {file: {type: 'file', name: 'ZZZ.tar.gz'}, extension: '.gz'},
  351. {file: {type: 'file', name: 'test.with.some.dots.in.it.txt'}, extension: '.txt'},
  352. // we render hidden files completely in gray
  353. {file: {type: 'file', name: '.test.with.some.dots.in.it.txt'}, extension: '.test.with.some.dots.in.it.txt'},
  354. {file: {type: 'file', name: '.hidden'}, extension: '.hidden'},
  355. ];
  356. fileList.setFiles(testFiles);
  357. for(var i = 0; i < testDataAndExpectedResult.length; i++) {
  358. var testSet = testDataAndExpectedResult[i];
  359. var fileData = testSet['file'];
  360. $tr = fileList.add(fileData);
  361. expect($tr.find('.nametext .extension').text()).toEqual(testSet['extension']);
  362. }
  363. });
  364. });
  365. describe('Removing files from the list', function() {
  366. it('Removes file from list when calling remove() and updates summary', function() {
  367. var $summary;
  368. var $removedEl;
  369. fileList.setFiles(testFiles);
  370. $removedEl = fileList.remove('One.txt');
  371. expect($removedEl).toBeDefined();
  372. expect($removedEl.attr('data-file')).toEqual('One.txt');
  373. expect($('#fileList tr').length).toEqual(3);
  374. expect(fileList.files.length).toEqual(3);
  375. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  376. $summary = $('#filestable .summary');
  377. expect($summary.hasClass('hidden')).toEqual(false);
  378. expect($summary.find('.info').text()).toEqual('1 folder and 2 files');
  379. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
  380. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  381. expect($summary.find('.filesize').text()).toEqual('69 kB');
  382. expect(fileList.isEmpty).toEqual(false);
  383. });
  384. it('Shows empty content when removing last file', function() {
  385. var $summary;
  386. fileList.setFiles([testFiles[0]]);
  387. fileList.remove('One.txt');
  388. expect($('#fileList tr').length).toEqual(0);
  389. expect(fileList.files.length).toEqual(0);
  390. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  391. $summary = $('#filestable .summary');
  392. expect($summary.hasClass('hidden')).toEqual(true);
  393. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  394. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  395. expect(fileList.isEmpty).toEqual(true);
  396. });
  397. });
  398. describe('Deleting files', function() {
  399. function doDelete() {
  400. var request, query;
  401. // note: normally called from FileActions
  402. fileList.do_delete(['One.txt', 'Two.jpg']);
  403. expect(fakeServer.requests.length).toEqual(1);
  404. request = fakeServer.requests[0];
  405. expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/delete.php');
  406. query = fakeServer.requests[0].requestBody;
  407. expect(OC.parseQueryString(query)).toEqual({'dir': '/subdir', files: '["One.txt","Two.jpg"]'});
  408. }
  409. it('calls delete.php, removes the deleted entries and updates summary', function() {
  410. var $summary;
  411. fileList.setFiles(testFiles);
  412. doDelete();
  413. fakeServer.requests[0].respond(
  414. 200,
  415. { 'Content-Type': 'application/json' },
  416. JSON.stringify({status: 'success'})
  417. );
  418. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  419. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  420. expect(fileList.findFileEl('Three.pdf').length).toEqual(1);
  421. expect(fileList.$fileList.find('tr').length).toEqual(2);
  422. $summary = $('#filestable .summary');
  423. expect($summary.hasClass('hidden')).toEqual(false);
  424. expect($summary.find('.info').text()).toEqual('1 folder and 1 file');
  425. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
  426. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  427. expect($summary.find('.filesize').text()).toEqual('57 kB');
  428. expect(fileList.isEmpty).toEqual(false);
  429. expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
  430. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  431. expect(notificationStub.notCalled).toEqual(true);
  432. });
  433. it('shows spinner on files to be deleted', function() {
  434. fileList.setFiles(testFiles);
  435. doDelete();
  436. expect(fileList.findFileEl('One.txt').find('.icon-loading-small:not(.icon-delete)').length).toEqual(1);
  437. expect(fileList.findFileEl('Three.pdf').find('.icon-delete:not(.icon-loading-small)').length).toEqual(1);
  438. });
  439. it('shows spinner on all files when deleting all', function() {
  440. fileList.setFiles(testFiles);
  441. fileList.do_delete();
  442. expect(fileList.$fileList.find('tr .icon-loading-small:not(.icon-delete)').length).toEqual(4);
  443. });
  444. it('updates summary when deleting last file', function() {
  445. var $summary;
  446. fileList.setFiles([testFiles[0], testFiles[1]]);
  447. doDelete();
  448. fakeServer.requests[0].respond(
  449. 200,
  450. { 'Content-Type': 'application/json' },
  451. JSON.stringify({status: 'success'})
  452. );
  453. expect(fileList.$fileList.find('tr').length).toEqual(0);
  454. $summary = $('#filestable .summary');
  455. expect($summary.hasClass('hidden')).toEqual(true);
  456. expect(fileList.isEmpty).toEqual(true);
  457. expect(fileList.files.length).toEqual(0);
  458. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  459. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  460. });
  461. it('bring back deleted item when delete call failed', function() {
  462. fileList.setFiles(testFiles);
  463. doDelete();
  464. fakeServer.requests[0].respond(
  465. 200,
  466. { 'Content-Type': 'application/json' },
  467. JSON.stringify({status: 'error', data: {message: 'WOOT'}})
  468. );
  469. // files are still in the list
  470. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  471. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  472. expect(fileList.$fileList.find('tr').length).toEqual(4);
  473. expect(notificationStub.calledOnce).toEqual(true);
  474. });
  475. });
  476. describe('Renaming files', function() {
  477. function doCancelRename() {
  478. var $input;
  479. for (var i = 0; i < testFiles.length; i++) {
  480. fileList.add(testFiles[i]);
  481. }
  482. // trigger rename prompt
  483. fileList.rename('One.txt');
  484. $input = fileList.$fileList.find('input.filename');
  485. // keep same name
  486. $input.val('One.txt');
  487. // trigger submit because triggering blur doesn't work in all browsers
  488. $input.closest('form').trigger('submit');
  489. expect(fakeServer.requests.length).toEqual(0);
  490. }
  491. function doRename() {
  492. var $input, request;
  493. for (var i = 0; i < testFiles.length; i++) {
  494. var file = testFiles[i];
  495. file.path = '/some/subdir';
  496. fileList.add(file, {silent: true});
  497. }
  498. // trigger rename prompt
  499. fileList.rename('One.txt');
  500. $input = fileList.$fileList.find('input.filename');
  501. $input.val('Tu_after_three.txt');
  502. // trigger submit because triggering blur doesn't work in all browsers
  503. $input.closest('form').trigger('submit');
  504. expect(fakeServer.requests.length).toEqual(1);
  505. request = fakeServer.requests[0];
  506. expect(request.url.substr(0, request.url.indexOf('?'))).toEqual(OC.webroot + '/index.php/apps/files/ajax/rename.php');
  507. expect(OC.parseQueryString(request.url)).toEqual({'dir': '/some/subdir', newname: 'Tu_after_three.txt', file: 'One.txt'});
  508. }
  509. it('Inserts renamed file entry at correct position if rename ajax call suceeded', function() {
  510. doRename();
  511. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  512. status: 'success',
  513. data: {
  514. name: 'Tu_after_three.txt',
  515. type: 'file'
  516. }
  517. }));
  518. // element stays renamed
  519. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  520. expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(1);
  521. expect(fileList.findFileEl('Tu_after_three.txt').index()).toEqual(2); // after Two.txt
  522. expect(alertStub.notCalled).toEqual(true);
  523. });
  524. it('Reverts file entry if rename ajax call failed', function() {
  525. doRename();
  526. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  527. status: 'error',
  528. data: {
  529. message: 'Something went wrong'
  530. }
  531. }));
  532. // element was reverted
  533. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  534. expect(fileList.findFileEl('One.txt').index()).toEqual(1); // after somedir
  535. expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(0);
  536. expect(alertStub.calledOnce).toEqual(true);
  537. });
  538. it('Correctly updates file link after rename', function() {
  539. var $tr;
  540. doRename();
  541. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  542. status: 'success',
  543. data: {
  544. name: 'Tu_after_three.txt'
  545. }
  546. }));
  547. $tr = fileList.findFileEl('Tu_after_three.txt');
  548. expect($tr.find('a.name').attr('href')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=Tu_after_three.txt');
  549. });
  550. it('Triggers "fileActionsReady" event after rename', function() {
  551. var handler = sinon.stub();
  552. fileList.$fileList.on('fileActionsReady', handler);
  553. doRename();
  554. expect(handler.notCalled).toEqual(true);
  555. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  556. status: 'success',
  557. data: {
  558. name: 'Tu_after_three.txt'
  559. }
  560. }));
  561. expect(handler.calledOnce).toEqual(true);
  562. expect(fileList.$fileList.find('.test').length).toEqual(0);
  563. });
  564. it('Leaves the summary alone when reinserting renamed element', function() {
  565. var $summary = $('#filestable .summary');
  566. doRename();
  567. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  568. status: 'success',
  569. data: {
  570. name: 'Tu_after_three.txt'
  571. }
  572. }));
  573. expect($summary.find('.info').text()).toEqual('1 folder and 3 files');
  574. });
  575. it('Leaves the summary alone when cancel renaming', function() {
  576. var $summary = $('#filestable .summary');
  577. doCancelRename();
  578. expect($summary.find('.info').text()).toEqual('1 folder and 3 files');
  579. });
  580. it('Hides actions while rename in progress', function() {
  581. var $tr;
  582. doRename();
  583. // element is renamed before the request finishes
  584. $tr = fileList.findFileEl('Tu_after_three.txt');
  585. expect($tr.length).toEqual(1);
  586. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  587. // file actions are hidden
  588. expect($tr.find('.action').hasClass('hidden')).toEqual(true);
  589. expect($tr.find('.fileactions').hasClass('hidden')).toEqual(true);
  590. // input and form are gone
  591. expect(fileList.$fileList.find('input.filename').length).toEqual(0);
  592. expect(fileList.$fileList.find('form').length).toEqual(0);
  593. });
  594. it('Validates the file name', function() {
  595. var $input, $tr;
  596. for (var i = 0; i < testFiles.length; i++) {
  597. fileList.add(testFiles[i], {silent: true});
  598. }
  599. // trigger rename prompt
  600. fileList.rename('One.txt');
  601. $input = fileList.$fileList.find('input.filename');
  602. $input.val('Two.jpg');
  603. // simulate key to trigger validation
  604. $input.trigger(new $.Event('keyup', {keyCode: 97}));
  605. // input is still there with error
  606. expect(fileList.$fileList.find('input.filename').length).toEqual(1);
  607. expect(fileList.$fileList.find('input.filename').hasClass('error')).toEqual(true);
  608. // trigger submit does not send server request
  609. $input.closest('form').trigger('submit');
  610. expect(fakeServer.requests.length).toEqual(0);
  611. // simulate escape key
  612. $input.trigger(new $.Event('keyup', {keyCode: 27}));
  613. // element is added back with the correct name
  614. $tr = fileList.findFileEl('One.txt');
  615. expect($tr.length).toEqual(1);
  616. expect($tr.find('a .nametext').text().trim()).toEqual('One.txt');
  617. expect($tr.find('a.name').is(':visible')).toEqual(true);
  618. $tr = fileList.findFileEl('Two.jpg');
  619. expect($tr.length).toEqual(1);
  620. expect($tr.find('a .nametext').text().trim()).toEqual('Two.jpg');
  621. expect($tr.find('a.name').is(':visible')).toEqual(true);
  622. // input and form are gone
  623. expect(fileList.$fileList.find('input.filename').length).toEqual(0);
  624. expect(fileList.$fileList.find('form').length).toEqual(0);
  625. });
  626. it('Restores thumbnail when rename was cancelled', function() {
  627. doRename();
  628. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('Tu_after_three.txt').find('.thumbnail')))
  629. .toEqual(OC.imagePath('core', 'loading.gif'));
  630. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  631. status: 'error',
  632. data: {
  633. message: 'Something went wrong'
  634. }
  635. }));
  636. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  637. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  638. .toEqual(OC.imagePath('core', 'filetypes/file.svg'));
  639. });
  640. });
  641. describe('Moving files', function() {
  642. beforeEach(function() {
  643. fileList.setFiles(testFiles);
  644. });
  645. it('Moves single file to target folder', function() {
  646. var request;
  647. fileList.move('One.txt', '/somedir');
  648. expect(fakeServer.requests.length).toEqual(1);
  649. request = fakeServer.requests[0];
  650. expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php');
  651. expect(OC.parseQueryString(request.requestBody)).toEqual({dir: '/subdir', file: 'One.txt', target: '/somedir'});
  652. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  653. status: 'success',
  654. data: {
  655. name: 'One.txt',
  656. type: 'file'
  657. }
  658. }));
  659. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  660. // folder size has increased
  661. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  662. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  663. expect(notificationStub.notCalled).toEqual(true);
  664. });
  665. it('Moves list of files to target folder', function() {
  666. var request;
  667. fileList.move(['One.txt', 'Two.jpg'], '/somedir');
  668. expect(fakeServer.requests.length).toEqual(2);
  669. request = fakeServer.requests[0];
  670. expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php');
  671. expect(OC.parseQueryString(request.requestBody)).toEqual({dir: '/subdir', file: 'One.txt', target: '/somedir'});
  672. request = fakeServer.requests[1];
  673. expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php');
  674. expect(OC.parseQueryString(request.requestBody)).toEqual({dir: '/subdir', file: 'Two.jpg', target: '/somedir'});
  675. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  676. status: 'success',
  677. data: {
  678. name: 'One.txt',
  679. type: 'file'
  680. }
  681. }));
  682. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  683. // folder size has increased
  684. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  685. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  686. fakeServer.requests[1].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  687. status: 'success',
  688. data: {
  689. name: 'Two.jpg',
  690. type: 'file'
  691. }
  692. }));
  693. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  694. // folder size has increased
  695. expect(fileList.findFileEl('somedir').data('size')).toEqual(12311);
  696. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 kB');
  697. expect(notificationStub.notCalled).toEqual(true);
  698. });
  699. it('Shows notification if a file could not be moved', function() {
  700. var request;
  701. fileList.move('One.txt', '/somedir');
  702. expect(fakeServer.requests.length).toEqual(1);
  703. request = fakeServer.requests[0];
  704. expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php');
  705. expect(OC.parseQueryString(request.requestBody)).toEqual({dir: '/subdir', file: 'One.txt', target: '/somedir'});
  706. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  707. status: 'error',
  708. data: {
  709. message: 'Error while moving file'
  710. }
  711. }));
  712. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  713. expect(notificationStub.calledOnce).toEqual(true);
  714. expect(notificationStub.getCall(0).args[0]).toEqual('Error while moving file');
  715. });
  716. it('Restores thumbnail if a file could not be moved', function() {
  717. var request;
  718. fileList.move('One.txt', '/somedir');
  719. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  720. .toEqual(OC.imagePath('core', 'loading.gif'));
  721. expect(fakeServer.requests.length).toEqual(1);
  722. request = fakeServer.requests[0];
  723. fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
  724. status: 'error',
  725. data: {
  726. message: 'Error while moving file'
  727. }
  728. }));
  729. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  730. expect(notificationStub.calledOnce).toEqual(true);
  731. expect(notificationStub.getCall(0).args[0]).toEqual('Error while moving file');
  732. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  733. .toEqual(OC.imagePath('core', 'filetypes/file.svg'));
  734. });
  735. });
  736. describe('List rendering', function() {
  737. it('renders a list of files using add()', function() {
  738. expect(fileList.files.length).toEqual(0);
  739. expect(fileList.files).toEqual([]);
  740. fileList.setFiles(testFiles);
  741. expect($('#fileList tr').length).toEqual(4);
  742. expect(fileList.files.length).toEqual(4);
  743. expect(fileList.files).toEqual(testFiles);
  744. });
  745. it('updates summary using the file sizes', function() {
  746. var $summary;
  747. fileList.setFiles(testFiles);
  748. $summary = $('#filestable .summary');
  749. expect($summary.hasClass('hidden')).toEqual(false);
  750. expect($summary.find('.info').text()).toEqual('1 folder and 3 files');
  751. expect($summary.find('.filesize').text()).toEqual('69 kB');
  752. });
  753. it('shows headers, summary and hide empty content message after setting files', function(){
  754. fileList.setFiles(testFiles);
  755. expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
  756. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  757. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(false);
  758. });
  759. it('hides headers, summary and show empty content message after setting empty file list', function(){
  760. fileList.setFiles([]);
  761. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  762. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  763. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
  764. });
  765. it('hides headers, empty content message, and summary when list is empty and user has no creation permission', function(){
  766. $('#permissions').val(0);
  767. fileList.setFiles([]);
  768. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  769. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  770. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
  771. });
  772. it('calling findFileEl() can find existing file element', function() {
  773. fileList.setFiles(testFiles);
  774. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  775. });
  776. it('calling findFileEl() returns empty when file not found in file', function() {
  777. fileList.setFiles(testFiles);
  778. expect(fileList.findFileEl('unexist.dat').length).toEqual(0);
  779. });
  780. it('only add file if in same current directory', function() {
  781. $('#dir').val('/current dir');
  782. var fileData = {
  783. type: 'file',
  784. name: 'testFile.txt',
  785. directory: '/current dir'
  786. };
  787. var $tr = fileList.add(fileData);
  788. expect(fileList.findFileEl('testFile.txt').length).toEqual(1);
  789. });
  790. it('triggers "fileActionsReady" event after update', function() {
  791. var handler = sinon.stub();
  792. fileList.$fileList.on('fileActionsReady', handler);
  793. fileList.setFiles(testFiles);
  794. expect(handler.calledOnce).toEqual(true);
  795. expect(handler.getCall(0).args[0].$files.length).toEqual(testFiles.length);
  796. });
  797. it('triggers "fileActionsReady" event after single add', function() {
  798. var handler = sinon.stub();
  799. var $tr;
  800. fileList.setFiles(testFiles);
  801. fileList.$fileList.on('fileActionsReady', handler);
  802. $tr = fileList.add({name: 'test.txt'});
  803. expect(handler.calledOnce).toEqual(true);
  804. expect(handler.getCall(0).args[0].$files.is($tr)).toEqual(true);
  805. });
  806. it('triggers "fileActionsReady" event after next page load with the newly appended files', function() {
  807. var handler = sinon.stub();
  808. fileList.setFiles(generateFiles(0, 64));
  809. fileList.$fileList.on('fileActionsReady', handler);
  810. fileList._nextPage();
  811. expect(handler.calledOnce).toEqual(true);
  812. expect(handler.getCall(0).args[0].$files.length).toEqual(fileList.pageSize());
  813. });
  814. it('does not trigger "fileActionsReady" event after single add with silent argument', function() {
  815. var handler = sinon.stub();
  816. fileList.setFiles(testFiles);
  817. fileList.$fileList.on('fileActionsReady', handler);
  818. fileList.add({name: 'test.txt'}, {silent: true});
  819. expect(handler.notCalled).toEqual(true);
  820. });
  821. it('triggers "updated" event after update', function() {
  822. var handler = sinon.stub();
  823. fileList.$fileList.on('updated', handler);
  824. fileList.setFiles(testFiles);
  825. expect(handler.calledOnce).toEqual(true);
  826. });
  827. it('does not update summary when removing non-existing files', function() {
  828. var $summary;
  829. // single file
  830. fileList.setFiles([testFiles[0]]);
  831. $summary = $('#filestable .summary');
  832. expect($summary.hasClass('hidden')).toEqual(false);
  833. expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
  834. fileList.remove('unexist.txt');
  835. expect($summary.hasClass('hidden')).toEqual(false);
  836. expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
  837. });
  838. });
  839. describe('Filtered list rendering', function() {
  840. it('filters the list of files using filter()', function() {
  841. expect(fileList.files.length).toEqual(0);
  842. expect(fileList.files).toEqual([]);
  843. fileList.setFiles(testFiles);
  844. var $summary = $('#filestable .summary');
  845. var $nofilterresults = fileList.$el.find(".nofilterresults");
  846. expect($nofilterresults.length).toEqual(1);
  847. expect($summary.hasClass('hidden')).toEqual(false);
  848. expect($('#fileList tr:not(.hidden)').length).toEqual(4);
  849. expect(fileList.files.length).toEqual(4);
  850. expect($summary.hasClass('hidden')).toEqual(false);
  851. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  852. fileList.setFilter('e');
  853. expect($('#fileList tr:not(.hidden)').length).toEqual(3);
  854. expect(fileList.files.length).toEqual(4);
  855. expect($summary.hasClass('hidden')).toEqual(false);
  856. expect($summary.find('.info').text()).toEqual("1 folder and 2 files match 'e'");
  857. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  858. fileList.setFilter('ee');
  859. expect($('#fileList tr:not(.hidden)').length).toEqual(1);
  860. expect(fileList.files.length).toEqual(4);
  861. expect($summary.hasClass('hidden')).toEqual(false);
  862. expect($summary.find('.info').text()).toEqual("0 folders and 1 file matches 'ee'");
  863. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  864. fileList.setFilter('eee');
  865. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  866. expect(fileList.files.length).toEqual(4);
  867. expect($summary.hasClass('hidden')).toEqual(true);
  868. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  869. fileList.setFilter('ee');
  870. expect($('#fileList tr:not(.hidden)').length).toEqual(1);
  871. expect(fileList.files.length).toEqual(4);
  872. expect($summary.hasClass('hidden')).toEqual(false);
  873. expect($summary.find('.info').text()).toEqual("0 folders and 1 file matches 'ee'");
  874. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  875. fileList.setFilter('e');
  876. expect($('#fileList tr:not(.hidden)').length).toEqual(3);
  877. expect(fileList.files.length).toEqual(4);
  878. expect($summary.hasClass('hidden')).toEqual(false);
  879. expect($summary.find('.info').text()).toEqual("1 folder and 2 files match 'e'");
  880. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  881. fileList.setFilter('');
  882. expect($('#fileList tr:not(.hidden)').length).toEqual(4);
  883. expect(fileList.files.length).toEqual(4);
  884. expect($summary.hasClass('hidden')).toEqual(false);
  885. expect($summary.find('.info').text()).toEqual("1 folder and 3 files");
  886. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  887. });
  888. it('hides the emptyfiles notice when using filter()', function() {
  889. expect(fileList.files.length).toEqual(0);
  890. expect(fileList.files).toEqual([]);
  891. fileList.setFiles([]);
  892. var $summary = $('#filestable .summary');
  893. var $emptycontent = fileList.$el.find("#emptycontent");
  894. var $nofilterresults = fileList.$el.find(".nofilterresults");
  895. expect($emptycontent.length).toEqual(1);
  896. expect($nofilterresults.length).toEqual(1);
  897. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  898. expect(fileList.files.length).toEqual(0);
  899. expect($summary.hasClass('hidden')).toEqual(true);
  900. expect($emptycontent.hasClass('hidden')).toEqual(false);
  901. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  902. fileList.setFilter('e');
  903. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  904. expect(fileList.files.length).toEqual(0);
  905. expect($summary.hasClass('hidden')).toEqual(true);
  906. expect($emptycontent.hasClass('hidden')).toEqual(true);
  907. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  908. fileList.setFilter('');
  909. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  910. expect(fileList.files.length).toEqual(0);
  911. expect($summary.hasClass('hidden')).toEqual(true);
  912. expect($emptycontent.hasClass('hidden')).toEqual(false);
  913. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  914. });
  915. it('does not show the emptyfiles or nofilterresults notice when the mask is active', function() {
  916. expect(fileList.files.length).toEqual(0);
  917. expect(fileList.files).toEqual([]);
  918. fileList.showMask();
  919. fileList.setFiles(testFiles);
  920. var $emptycontent = fileList.$el.find("#emptycontent");
  921. var $nofilterresults = fileList.$el.find(".nofilterresults");
  922. expect($emptycontent.length).toEqual(1);
  923. expect($nofilterresults.length).toEqual(1);
  924. expect($emptycontent.hasClass('hidden')).toEqual(true);
  925. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  926. /*
  927. fileList.setFilter('e');
  928. expect($emptycontent.hasClass('hidden')).toEqual(true);
  929. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  930. */
  931. fileList.setFilter('');
  932. expect($emptycontent.hasClass('hidden')).toEqual(true);
  933. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  934. });
  935. });
  936. describe('Rendering next page on scroll', function() {
  937. beforeEach(function() {
  938. fileList.setFiles(generateFiles(0, 64));
  939. });
  940. it('renders only the first page', function() {
  941. expect(fileList.files.length).toEqual(65);
  942. expect($('#fileList tr').length).toEqual(20);
  943. });
  944. it('renders the second page when scrolling down (trigger nextPage)', function() {
  945. // TODO: can't simulate scrolling here, so calling nextPage directly
  946. fileList._nextPage(true);
  947. expect($('#fileList tr').length).toEqual(40);
  948. fileList._nextPage(true);
  949. expect($('#fileList tr').length).toEqual(60);
  950. fileList._nextPage(true);
  951. expect($('#fileList tr').length).toEqual(65);
  952. fileList._nextPage(true);
  953. // stays at 65
  954. expect($('#fileList tr').length).toEqual(65);
  955. });
  956. it('inserts into the DOM if insertion point is in the visible page ', function() {
  957. fileList.add({
  958. id: 2000,
  959. type: 'file',
  960. name: 'File with index 15b.txt'
  961. });
  962. expect($('#fileList tr').length).toEqual(21);
  963. expect(fileList.findFileEl('File with index 15b.txt').index()).toEqual(16);
  964. });
  965. it('does not inserts into the DOM if insertion point is not the visible page ', function() {
  966. fileList.add({
  967. id: 2000,
  968. type: 'file',
  969. name: 'File with index 28b.txt'
  970. });
  971. expect($('#fileList tr').length).toEqual(20);
  972. expect(fileList.findFileEl('File with index 28b.txt').length).toEqual(0);
  973. fileList._nextPage(true);
  974. expect($('#fileList tr').length).toEqual(40);
  975. expect(fileList.findFileEl('File with index 28b.txt').index()).toEqual(29);
  976. });
  977. it('appends into the DOM when inserting a file after the last visible element', function() {
  978. fileList.add({
  979. id: 2000,
  980. type: 'file',
  981. name: 'File with index 19b.txt'
  982. });
  983. expect($('#fileList tr').length).toEqual(21);
  984. fileList._nextPage(true);
  985. expect($('#fileList tr').length).toEqual(41);
  986. });
  987. it('appends into the DOM when inserting a file on the last page when visible', function() {
  988. fileList._nextPage(true);
  989. expect($('#fileList tr').length).toEqual(40);
  990. fileList._nextPage(true);
  991. expect($('#fileList tr').length).toEqual(60);
  992. fileList._nextPage(true);
  993. expect($('#fileList tr').length).toEqual(65);
  994. fileList._nextPage(true);
  995. fileList.add({
  996. id: 2000,
  997. type: 'file',
  998. name: 'File with index 88.txt'
  999. });
  1000. expect($('#fileList tr').length).toEqual(66);
  1001. fileList._nextPage(true);
  1002. expect($('#fileList tr').length).toEqual(66);
  1003. });
  1004. it('shows additional page when appending a page of files and scrolling down', function() {
  1005. var newFiles = generateFiles(66, 81);
  1006. for (var i = 0; i < newFiles.length; i++) {
  1007. fileList.add(newFiles[i]);
  1008. }
  1009. expect($('#fileList tr').length).toEqual(20);
  1010. fileList._nextPage(true);
  1011. expect($('#fileList tr').length).toEqual(40);
  1012. fileList._nextPage(true);
  1013. expect($('#fileList tr').length).toEqual(60);
  1014. fileList._nextPage(true);
  1015. expect($('#fileList tr').length).toEqual(80);
  1016. fileList._nextPage(true);
  1017. expect($('#fileList tr').length).toEqual(81);
  1018. fileList._nextPage(true);
  1019. expect($('#fileList tr').length).toEqual(81);
  1020. });
  1021. it('automatically renders next page when there are not enough elements visible', function() {
  1022. // delete the 15 first elements
  1023. for (var i = 0; i < 15; i++) {
  1024. fileList.remove(fileList.files[0].name);
  1025. }
  1026. // still makes sure that there are 20 elements visible, if any
  1027. expect($('#fileList tr').length).toEqual(25);
  1028. });
  1029. });
  1030. describe('file previews', function() {
  1031. var previewLoadStub;
  1032. beforeEach(function() {
  1033. previewLoadStub = sinon.stub(OCA.Files.FileList.prototype, 'lazyLoadPreview');
  1034. });
  1035. afterEach(function() {
  1036. previewLoadStub.restore();
  1037. });
  1038. it('renders default icon for file when none provided and no preview is available', function() {
  1039. var fileData = {
  1040. type: 'file',
  1041. name: 'testFile.txt'
  1042. };
  1043. var $tr = fileList.add(fileData);
  1044. var $imgDiv = $tr.find('td.filename .thumbnail');
  1045. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
  1046. expect(previewLoadStub.notCalled).toEqual(true);
  1047. });
  1048. it('renders default icon for dir when none provided and no preview is available', function() {
  1049. var fileData = {
  1050. type: 'dir',
  1051. name: 'test dir'
  1052. };
  1053. var $tr = fileList.add(fileData);
  1054. var $imgDiv = $tr.find('td.filename .thumbnail');
  1055. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/folder.svg');
  1056. expect(previewLoadStub.notCalled).toEqual(true);
  1057. });
  1058. it('renders provided icon for file when provided', function() {
  1059. var fileData = {
  1060. type: 'file',
  1061. name: 'test dir',
  1062. icon: OC.webroot + '/core/img/filetypes/application-pdf.svg'
  1063. };
  1064. var $tr = fileList.add(fileData);
  1065. var $imgDiv = $tr.find('td.filename .thumbnail');
  1066. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/application-pdf.svg');
  1067. expect(previewLoadStub.notCalled).toEqual(true);
  1068. });
  1069. it('renders preview when no icon was provided and preview is available', function() {
  1070. var fileData = {
  1071. type: 'file',
  1072. name: 'test dir',
  1073. isPreviewAvailable: true
  1074. };
  1075. var $tr = fileList.add(fileData);
  1076. var $td = $tr.find('td.filename');
  1077. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
  1078. expect(previewLoadStub.calledOnce).toEqual(true);
  1079. // third argument is callback
  1080. previewLoadStub.getCall(0).args[0].callback(OC.webroot + '/somepath.png');
  1081. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/somepath.png');
  1082. });
  1083. it('renders default file type icon when no icon was provided and no preview is available', function() {
  1084. var fileData = {
  1085. type: 'file',
  1086. name: 'test dir',
  1087. isPreviewAvailable: false
  1088. };
  1089. var $tr = fileList.add(fileData);
  1090. var $imgDiv = $tr.find('td.filename .thumbnail');
  1091. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
  1092. expect(previewLoadStub.notCalled).toEqual(true);
  1093. });
  1094. });
  1095. describe('viewer mode', function() {
  1096. it('enabling viewer mode hides files table and action buttons', function() {
  1097. fileList.setViewerMode(true);
  1098. expect($('#filestable').hasClass('hidden')).toEqual(true);
  1099. expect($('.actions').hasClass('hidden')).toEqual(true);
  1100. expect($('.notCreatable').hasClass('hidden')).toEqual(true);
  1101. });
  1102. it('disabling viewer mode restores files table and action buttons', function() {
  1103. fileList.setViewerMode(true);
  1104. fileList.setViewerMode(false);
  1105. expect($('#filestable').hasClass('hidden')).toEqual(false);
  1106. expect($('.actions').hasClass('hidden')).toEqual(false);
  1107. expect($('.notCreatable').hasClass('hidden')).toEqual(true);
  1108. });
  1109. it('disabling viewer mode restores files table and action buttons with correct permissions', function() {
  1110. $('#permissions').val(0);
  1111. fileList.setViewerMode(true);
  1112. fileList.setViewerMode(false);
  1113. expect($('#filestable').hasClass('hidden')).toEqual(false);
  1114. expect($('.actions').hasClass('hidden')).toEqual(true);
  1115. expect($('.notCreatable').hasClass('hidden')).toEqual(false);
  1116. });
  1117. it('toggling viewer mode triggers event', function() {
  1118. var handler = sinon.stub();
  1119. fileList.$el.on('changeViewerMode', handler);
  1120. fileList.setViewerMode(true);
  1121. expect(handler.calledOnce).toEqual(true);
  1122. expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(true);
  1123. handler.reset();
  1124. fileList.setViewerMode(false);
  1125. expect(handler.calledOnce).toEqual(true);
  1126. expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(false);
  1127. });
  1128. });
  1129. describe('loading file list', function() {
  1130. beforeEach(function() {
  1131. var data = {
  1132. status: 'success',
  1133. data: {
  1134. files: testFiles,
  1135. permissions: 31
  1136. }
  1137. };
  1138. fakeServer.respondWith(/\/index\.php\/apps\/files\/ajax\/list.php\?dir=%2F(subdir|anothersubdir)/, [
  1139. 200, {
  1140. "Content-Type": "application/json"
  1141. },
  1142. JSON.stringify(data)
  1143. ]);
  1144. });
  1145. it('fetches file list from server and renders it when reload() is called', function() {
  1146. fileList.reload();
  1147. expect(fakeServer.requests.length).toEqual(1);
  1148. var url = fakeServer.requests[0].url;
  1149. var query = url.substr(url.indexOf('?') + 1);
  1150. expect(OC.parseQueryString(query)).toEqual({'dir': '/subdir', sort: 'name', sortdirection: 'asc'});
  1151. fakeServer.respond();
  1152. expect($('#fileList tr').length).toEqual(4);
  1153. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  1154. });
  1155. it('switches dir and fetches file list when calling changeDirectory()', function() {
  1156. fileList.changeDirectory('/anothersubdir');
  1157. expect(fileList.getCurrentDirectory()).toEqual('/anothersubdir');
  1158. expect(fakeServer.requests.length).toEqual(1);
  1159. var url = fakeServer.requests[0].url;
  1160. var query = url.substr(url.indexOf('?') + 1);
  1161. expect(OC.parseQueryString(query)).toEqual({'dir': '/anothersubdir', sort: 'name', sortdirection: 'asc'});
  1162. fakeServer.respond();
  1163. });
  1164. it('converts backslashes to slashes when calling changeDirectory()', function() {
  1165. fileList.changeDirectory('/another\\subdir');
  1166. expect(fileList.getCurrentDirectory()).toEqual('/another/subdir');
  1167. });
  1168. it('switches to root dir when current directory does not exist', function() {
  1169. fakeServer.respondWith(/\/index\.php\/apps\/files\/ajax\/list.php\?dir=%2funexist/, [
  1170. 404, {
  1171. "Content-Type": "application/json"
  1172. },
  1173. ''
  1174. ]);
  1175. fileList.changeDirectory('/unexist');
  1176. fakeServer.respond();
  1177. expect(fileList.getCurrentDirectory()).toEqual('/');
  1178. });
  1179. it('shows mask before loading file list then hides it at the end', function() {
  1180. var showMaskStub = sinon.stub(fileList, 'showMask');
  1181. var hideMaskStub = sinon.stub(fileList, 'hideMask');
  1182. fileList.changeDirectory('/anothersubdir');
  1183. expect(showMaskStub.calledOnce).toEqual(true);
  1184. expect(hideMaskStub.calledOnce).toEqual(false);
  1185. fakeServer.respond();
  1186. expect(showMaskStub.calledOnce).toEqual(true);
  1187. expect(hideMaskStub.calledOnce).toEqual(true);
  1188. showMaskStub.restore();
  1189. hideMaskStub.restore();
  1190. });
  1191. it('triggers "changeDirectory" event when changing directory', function() {
  1192. var handler = sinon.stub();
  1193. $('#app-content-files').on('changeDirectory', handler);
  1194. fileList.changeDirectory('/somedir');
  1195. expect(handler.calledOnce).toEqual(true);
  1196. expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
  1197. });
  1198. it('changes the directory when receiving "urlChanged" event', function() {
  1199. $('#app-content-files').trigger(new $.Event('urlChanged', {view: 'files', dir: '/somedir'}));
  1200. expect(fileList.getCurrentDirectory()).toEqual('/somedir');
  1201. });
  1202. it('refreshes breadcrumb after update', function() {
  1203. var setDirSpy = sinon.spy(fileList.breadcrumb, 'setDirectory');
  1204. fileList.changeDirectory('/anothersubdir');
  1205. fakeServer.respond();
  1206. expect(fileList.breadcrumb.setDirectory.calledOnce).toEqual(true);
  1207. expect(fileList.breadcrumb.setDirectory.calledWith('/anothersubdir')).toEqual(true);
  1208. setDirSpy.restore();
  1209. });
  1210. });
  1211. describe('breadcrumb events', function() {
  1212. beforeEach(function() {
  1213. var data = {
  1214. status: 'success',
  1215. data: {
  1216. files: testFiles,
  1217. permissions: 31
  1218. }
  1219. };
  1220. fakeServer.respondWith(/\/index\.php\/apps\/files\/ajax\/list.php\?dir=%2Fsubdir/, [
  1221. 200, {
  1222. "Content-Type": "application/json"
  1223. },
  1224. JSON.stringify(data)
  1225. ]);
  1226. });
  1227. it('clicking on root breadcrumb changes directory to root', function() {
  1228. fileList.changeDirectory('/subdir/two/three with space/four/five');
  1229. fakeServer.respond();
  1230. var changeDirStub = sinon.stub(fileList, 'changeDirectory');
  1231. fileList.breadcrumb.$el.find('.crumb:eq(0)').trigger({type: 'click', which: 1});
  1232. expect(changeDirStub.calledOnce).toEqual(true);
  1233. expect(changeDirStub.getCall(0).args[0]).toEqual('/');
  1234. changeDirStub.restore();
  1235. });
  1236. it('clicking on breadcrumb changes directory', function() {
  1237. fileList.changeDirectory('/subdir/two/three with space/four/five');
  1238. fakeServer.respond();
  1239. var changeDirStub = sinon.stub(fileList, 'changeDirectory');
  1240. fileList.breadcrumb.$el.find('.crumb:eq(3)').trigger({type: 'click', which: 1});
  1241. expect(changeDirStub.calledOnce).toEqual(true);
  1242. expect(changeDirStub.getCall(0).args[0]).toEqual('/subdir/two/three with space');
  1243. changeDirStub.restore();
  1244. });
  1245. it('dropping files on breadcrumb calls move operation', function() {
  1246. var request, query, testDir = '/subdir/two/three with space/four/five';
  1247. fileList.changeDirectory(testDir);
  1248. fakeServer.respond();
  1249. var $crumb = fileList.breadcrumb.$el.find('.crumb:eq(3)');
  1250. // no idea what this is but is required by the handler
  1251. var ui = {
  1252. helper: {
  1253. find: sinon.stub()
  1254. }
  1255. };
  1256. // returns a list of tr that were dragged
  1257. ui.helper.find.returns([
  1258. $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
  1259. $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
  1260. ]);
  1261. // simulate drop event
  1262. fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui);
  1263. // will trigger two calls to move.php (first one was previous list.php)
  1264. expect(fakeServer.requests.length).toEqual(3);
  1265. request = fakeServer.requests[1];
  1266. expect(request.method).toEqual('POST');
  1267. expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php');
  1268. query = OC.parseQueryString(request.requestBody);
  1269. expect(query).toEqual({
  1270. target: '/subdir/two/three with space',
  1271. dir: testDir,
  1272. file: 'One.txt'
  1273. });
  1274. request = fakeServer.requests[2];
  1275. expect(request.method).toEqual('POST');
  1276. expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/move.php');
  1277. query = OC.parseQueryString(request.requestBody);
  1278. expect(query).toEqual({
  1279. target: '/subdir/two/three with space',
  1280. dir: testDir,
  1281. file: 'Two.jpg'
  1282. });
  1283. });
  1284. it('dropping files on same dir breadcrumb does nothing', function() {
  1285. var testDir = '/subdir/two/three with space/four/five';
  1286. fileList.changeDirectory(testDir);
  1287. fakeServer.respond();
  1288. var $crumb = fileList.breadcrumb.$el.find('.crumb:last');
  1289. // no idea what this is but is required by the handler
  1290. var ui = {
  1291. helper: {
  1292. find: sinon.stub()
  1293. }
  1294. };
  1295. // returns a list of tr that were dragged
  1296. ui.helper.find.returns([
  1297. $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
  1298. $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
  1299. ]);
  1300. // simulate drop event
  1301. fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui);
  1302. // no extra server request
  1303. expect(fakeServer.requests.length).toEqual(1);
  1304. });
  1305. });
  1306. describe('Download Url', function() {
  1307. it('returns correct download URL for single files', function() {
  1308. expect(fileList.getDownloadUrl('some file.txt')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=some%20file.txt');
  1309. expect(fileList.getDownloadUrl('some file.txt', '/anotherpath/abc')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fanotherpath%2Fabc&files=some%20file.txt');
  1310. $('#dir').val('/');
  1311. expect(fileList.getDownloadUrl('some file.txt')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=some%20file.txt');
  1312. });
  1313. it('returns correct download URL for multiple files', function() {
  1314. expect(fileList.getDownloadUrl(['a b c.txt', 'd e f.txt'])).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22a%20b%20c.txt%22%2C%22d%20e%20f.txt%22%5D');
  1315. });
  1316. it('returns the correct ajax URL', function() {
  1317. expect(fileList.getAjaxUrl('test', {a:1, b:'x y'})).toEqual(OC.webroot + '/index.php/apps/files/ajax/test.php?a=1&b=x%20y');
  1318. });
  1319. });
  1320. describe('File selection', function() {
  1321. beforeEach(function() {
  1322. fileList.setFiles(testFiles);
  1323. });
  1324. it('Selects a file when clicking its checkbox', function() {
  1325. var $tr = fileList.findFileEl('One.txt');
  1326. expect($tr.find('input:checkbox').prop('checked')).toEqual(false);
  1327. $tr.find('td.filename input:checkbox').click();
  1328. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1329. });
  1330. it('Selects/deselect a file when clicking on the name while holding Ctrl', function() {
  1331. var $tr = fileList.findFileEl('One.txt');
  1332. var $tr2 = fileList.findFileEl('Three.pdf');
  1333. var e;
  1334. expect($tr.find('input:checkbox').prop('checked')).toEqual(false);
  1335. expect($tr2.find('input:checkbox').prop('checked')).toEqual(false);
  1336. e = new $.Event('click');
  1337. e.ctrlKey = true;
  1338. $tr.find('td.filename .name').trigger(e);
  1339. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1340. expect($tr2.find('input:checkbox').prop('checked')).toEqual(false);
  1341. // click on second entry, does not clear the selection
  1342. e = new $.Event('click');
  1343. e.ctrlKey = true;
  1344. $tr2.find('td.filename .name').trigger(e);
  1345. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1346. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1347. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toEqual(['One.txt', 'Three.pdf']);
  1348. // deselect now
  1349. e = new $.Event('click');
  1350. e.ctrlKey = true;
  1351. $tr2.find('td.filename .name').trigger(e);
  1352. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1353. expect($tr2.find('input:checkbox').prop('checked')).toEqual(false);
  1354. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toEqual(['One.txt']);
  1355. });
  1356. it('Selects a range when clicking on one file then Shift clicking on another one', function() {
  1357. var $tr = fileList.findFileEl('One.txt');
  1358. var $tr2 = fileList.findFileEl('Three.pdf');
  1359. var e;
  1360. $tr.find('td.filename input:checkbox').click();
  1361. e = new $.Event('click');
  1362. e.shiftKey = true;
  1363. $tr2.find('td.filename .name').trigger(e);
  1364. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1365. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1366. expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
  1367. var selection = _.pluck(fileList.getSelectedFiles(), 'name');
  1368. expect(selection.length).toEqual(3);
  1369. expect(selection).toContain('One.txt');
  1370. expect(selection).toContain('Two.jpg');
  1371. expect(selection).toContain('Three.pdf');
  1372. });
  1373. it('Selects a range when clicking on one file then Shift clicking on another one that is above the first one', function() {
  1374. var $tr = fileList.findFileEl('One.txt');
  1375. var $tr2 = fileList.findFileEl('Three.pdf');
  1376. var e;
  1377. $tr2.find('td.filename input:checkbox').click();
  1378. e = new $.Event('click');
  1379. e.shiftKey = true;
  1380. $tr.find('td.filename .name').trigger(e);
  1381. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1382. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1383. expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
  1384. var selection = _.pluck(fileList.getSelectedFiles(), 'name');
  1385. expect(selection.length).toEqual(3);
  1386. expect(selection).toContain('One.txt');
  1387. expect(selection).toContain('Two.jpg');
  1388. expect(selection).toContain('Three.pdf');
  1389. });
  1390. it('Selecting all files will automatically check "select all" checkbox', function() {
  1391. expect($('.select-all').prop('checked')).toEqual(false);
  1392. $('#fileList tr td.filename input:checkbox').click();
  1393. expect($('.select-all').prop('checked')).toEqual(true);
  1394. });
  1395. it('Selecting all files on the first visible page will not automatically check "select all" checkbox', function() {
  1396. fileList.setFiles(generateFiles(0, 41));
  1397. expect($('.select-all').prop('checked')).toEqual(false);
  1398. $('#fileList tr td.filename input:checkbox').click();
  1399. expect($('.select-all').prop('checked')).toEqual(false);
  1400. });
  1401. it('Clicking "select all" will select/deselect all files', function() {
  1402. fileList.setFiles(generateFiles(0, 41));
  1403. $('.select-all').click();
  1404. expect($('.select-all').prop('checked')).toEqual(true);
  1405. $('#fileList tr input:checkbox').each(function() {
  1406. expect($(this).prop('checked')).toEqual(true);
  1407. });
  1408. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
  1409. $('.select-all').click();
  1410. expect($('.select-all').prop('checked')).toEqual(false);
  1411. $('#fileList tr input:checkbox').each(function() {
  1412. expect($(this).prop('checked')).toEqual(false);
  1413. });
  1414. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(0);
  1415. });
  1416. it('Clicking "select all" then deselecting a file will uncheck "select all"', function() {
  1417. $('.select-all').click();
  1418. expect($('.select-all').prop('checked')).toEqual(true);
  1419. var $tr = fileList.findFileEl('One.txt');
  1420. $tr.find('input:checkbox').click();
  1421. expect($('.select-all').prop('checked')).toEqual(false);
  1422. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1423. });
  1424. it('Updates the selection summary when doing a few manipulations with "Select all"', function() {
  1425. $('.select-all').click();
  1426. expect($('.select-all').prop('checked')).toEqual(true);
  1427. var $tr = fileList.findFileEl('One.txt');
  1428. // unselect one
  1429. $tr.find('input:checkbox').click();
  1430. expect($('.select-all').prop('checked')).toEqual(false);
  1431. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1432. // select all
  1433. $('.select-all').click();
  1434. expect($('.select-all').prop('checked')).toEqual(true);
  1435. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
  1436. // unselect one
  1437. $tr.find('input:checkbox').click();
  1438. expect($('.select-all').prop('checked')).toEqual(false);
  1439. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1440. // re-select it
  1441. $tr.find('input:checkbox').click();
  1442. expect($('.select-all').prop('checked')).toEqual(true);
  1443. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
  1444. });
  1445. it('Auto-selects files on next page when "select all" is checked', function() {
  1446. fileList.setFiles(generateFiles(0, 41));
  1447. $('.select-all').click();
  1448. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(20);
  1449. fileList._nextPage(true);
  1450. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(40);
  1451. fileList._nextPage(true);
  1452. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(42);
  1453. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
  1454. });
  1455. it('Selecting files updates selection summary', function() {
  1456. var $summary = $('#headerName a.name>span:first');
  1457. expect($summary.text()).toEqual('Name');
  1458. fileList.findFileEl('One.txt').find('input:checkbox').click();
  1459. fileList.findFileEl('Three.pdf').find('input:checkbox').click();
  1460. fileList.findFileEl('somedir').find('input:checkbox').click();
  1461. expect($summary.text()).toEqual('1 folder & 2 files');
  1462. });
  1463. it('Unselecting files hides selection summary', function() {
  1464. var $summary = $('#headerName a.name>span:first');
  1465. fileList.findFileEl('One.txt').find('input:checkbox').click().click();
  1466. expect($summary.text()).toEqual('Name');
  1467. });
  1468. it('Select/deselect files shows/hides file actions', function() {
  1469. var $actions = $('#headerName .selectedActions');
  1470. var $checkbox = fileList.findFileEl('One.txt').find('input:checkbox');
  1471. expect($actions.hasClass('hidden')).toEqual(true);
  1472. $checkbox.click();
  1473. expect($actions.hasClass('hidden')).toEqual(false);
  1474. $checkbox.click();
  1475. expect($actions.hasClass('hidden')).toEqual(true);
  1476. });
  1477. it('Selection is cleared when switching dirs', function() {
  1478. $('.select-all').click();
  1479. var data = {
  1480. status: 'success',
  1481. data: {
  1482. files: testFiles,
  1483. permissions: 31
  1484. }
  1485. };
  1486. fakeServer.respondWith(/\/index\.php\/apps\/files\/ajax\/list.php/, [
  1487. 200, {
  1488. "Content-Type": "application/json"
  1489. },
  1490. JSON.stringify(data)
  1491. ]
  1492. );
  1493. fileList.changeDirectory('/');
  1494. fakeServer.respond();
  1495. expect($('.select-all').prop('checked')).toEqual(false);
  1496. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toEqual([]);
  1497. });
  1498. it('getSelectedFiles returns the selected files even when they are on the next page', function() {
  1499. var selectedFiles;
  1500. fileList.setFiles(generateFiles(0, 41));
  1501. $('.select-all').click();
  1502. // unselect one to not have the "allFiles" case
  1503. fileList.$fileList.find('tr input:checkbox:first').click();
  1504. // only 20 files visible, must still return all the selected ones
  1505. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1506. expect(selectedFiles.length).toEqual(41);
  1507. });
  1508. describe('clearing the selection', function() {
  1509. it('clears selected files selected individually calling setFiles()', function() {
  1510. var selectedFiles;
  1511. fileList.setFiles(generateFiles(0, 41));
  1512. fileList.$fileList.find('tr:eq(5) input:checkbox:first').click();
  1513. fileList.$fileList.find('tr:eq(7) input:checkbox:first').click();
  1514. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1515. expect(selectedFiles.length).toEqual(2);
  1516. fileList.setFiles(generateFiles(0, 2));
  1517. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1518. expect(selectedFiles.length).toEqual(0);
  1519. });
  1520. it('clears selected files selected with select all when calling setFiles()', function() {
  1521. var selectedFiles;
  1522. fileList.setFiles(generateFiles(0, 41));
  1523. $('.select-all').click();
  1524. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1525. expect(selectedFiles.length).toEqual(42);
  1526. fileList.setFiles(generateFiles(0, 2));
  1527. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1528. expect(selectedFiles.length).toEqual(0);
  1529. });
  1530. });
  1531. describe('Selection overlay', function() {
  1532. it('show delete action according to directory permissions', function() {
  1533. fileList.setFiles(testFiles);
  1534. $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_DELETE);
  1535. $('.select-all').click();
  1536. expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(false);
  1537. $('.select-all').click();
  1538. $('#permissions').val(OC.PERMISSION_READ);
  1539. $('.select-all').click();
  1540. expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(true);
  1541. });
  1542. it('show doesnt show the delete action if one or more files are not deletable', function () {
  1543. fileList.setFiles(testFiles);
  1544. $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_DELETE);
  1545. $('.select-all').click();
  1546. expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(false);
  1547. testFiles[0].permissions = OC.PERMISSION_READ;
  1548. $('.select-all').click();
  1549. fileList.setFiles(testFiles);
  1550. $('.select-all').click();
  1551. expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(true);
  1552. });
  1553. });
  1554. describe('Actions', function() {
  1555. beforeEach(function() {
  1556. fileList.findFileEl('One.txt').find('input:checkbox').click();
  1557. fileList.findFileEl('Three.pdf').find('input:checkbox').click();
  1558. fileList.findFileEl('somedir').find('input:checkbox').click();
  1559. });
  1560. it('getSelectedFiles returns the selected file data', function() {
  1561. var files = fileList.getSelectedFiles();
  1562. expect(files.length).toEqual(3);
  1563. expect(files[0]).toEqual({
  1564. id: 1,
  1565. name: 'One.txt',
  1566. mimetype: 'text/plain',
  1567. type: 'file',
  1568. size: 12,
  1569. etag: 'abc',
  1570. permissions: OC.PERMISSION_ALL
  1571. });
  1572. expect(files[1]).toEqual({
  1573. id: 3,
  1574. type: 'file',
  1575. name: 'Three.pdf',
  1576. mimetype: 'application/pdf',
  1577. size: 58009,
  1578. etag: '123',
  1579. permissions: OC.PERMISSION_ALL
  1580. });
  1581. expect(files[2]).toEqual({
  1582. id: 4,
  1583. type: 'dir',
  1584. name: 'somedir',
  1585. mimetype: 'httpd/unix-directory',
  1586. size: 250,
  1587. etag: '456',
  1588. permissions: OC.PERMISSION_ALL
  1589. });
  1590. });
  1591. it('Removing a file removes it from the selection', function() {
  1592. fileList.remove('Three.pdf');
  1593. var files = fileList.getSelectedFiles();
  1594. expect(files.length).toEqual(2);
  1595. expect(files[0]).toEqual({
  1596. id: 1,
  1597. name: 'One.txt',
  1598. mimetype: 'text/plain',
  1599. type: 'file',
  1600. size: 12,
  1601. etag: 'abc',
  1602. permissions: OC.PERMISSION_ALL
  1603. });
  1604. expect(files[1]).toEqual({
  1605. id: 4,
  1606. type: 'dir',
  1607. name: 'somedir',
  1608. mimetype: 'httpd/unix-directory',
  1609. size: 250,
  1610. etag: '456',
  1611. permissions: OC.PERMISSION_ALL
  1612. });
  1613. });
  1614. describe('Download', function() {
  1615. it('Opens download URL when clicking "Download"', function() {
  1616. var redirectStub = sinon.stub(OC, 'redirect');
  1617. $('.selectedActions .download').click();
  1618. expect(redirectStub.calledOnce).toEqual(true);
  1619. expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D');
  1620. redirectStub.restore();
  1621. });
  1622. it('Downloads root folder when all selected in root folder', function() {
  1623. $('#dir').val('/');
  1624. $('.select-all').click();
  1625. var redirectStub = sinon.stub(OC, 'redirect');
  1626. $('.selectedActions .download').click();
  1627. expect(redirectStub.calledOnce).toEqual(true);
  1628. expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=');
  1629. redirectStub.restore();
  1630. });
  1631. it('Downloads parent folder when all selected in subfolder', function() {
  1632. $('.select-all').click();
  1633. var redirectStub = sinon.stub(OC, 'redirect');
  1634. $('.selectedActions .download').click();
  1635. expect(redirectStub.calledOnce).toEqual(true);
  1636. expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir');
  1637. redirectStub.restore();
  1638. });
  1639. });
  1640. describe('Delete', function() {
  1641. it('Deletes selected files when "Delete" clicked', function() {
  1642. var request;
  1643. $('.selectedActions .delete-selected').click();
  1644. expect(fakeServer.requests.length).toEqual(1);
  1645. request = fakeServer.requests[0];
  1646. expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/delete.php');
  1647. expect(OC.parseQueryString(request.requestBody))
  1648. .toEqual({'dir': '/subdir', files: '["One.txt","Three.pdf","somedir"]'});
  1649. fakeServer.requests[0].respond(
  1650. 200,
  1651. { 'Content-Type': 'application/json' },
  1652. JSON.stringify({status: 'success'})
  1653. );
  1654. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  1655. expect(fileList.findFileEl('Three.pdf').length).toEqual(0);
  1656. expect(fileList.findFileEl('somedir').length).toEqual(0);
  1657. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  1658. });
  1659. it('Deletes all files when all selected when "Delete" clicked', function() {
  1660. var request;
  1661. $('.select-all').click();
  1662. $('.selectedActions .delete-selected').click();
  1663. expect(fakeServer.requests.length).toEqual(1);
  1664. request = fakeServer.requests[0];
  1665. expect(request.url).toEqual(OC.webroot + '/index.php/apps/files/ajax/delete.php');
  1666. expect(OC.parseQueryString(request.requestBody))
  1667. .toEqual({'dir': '/subdir', allfiles: 'true'});
  1668. fakeServer.requests[0].respond(
  1669. 200,
  1670. { 'Content-Type': 'application/json' },
  1671. JSON.stringify({status: 'success'})
  1672. );
  1673. expect(fileList.isEmpty).toEqual(true);
  1674. });
  1675. });
  1676. });
  1677. it('resets the file selection on reload', function() {
  1678. fileList.$el.find('.select-all').click();
  1679. fileList.reload();
  1680. expect(fileList.$el.find('.select-all').prop('checked')).toEqual(false);
  1681. expect(fileList.getSelectedFiles()).toEqual([]);
  1682. });
  1683. describe('Disabled selection', function() {
  1684. beforeEach(function() {
  1685. fileList._allowSelection = false;
  1686. fileList.setFiles(testFiles);
  1687. });
  1688. it('Does not render checkboxes', function() {
  1689. expect(fileList.$fileList.find('.selectCheckBox').length).toEqual(0);
  1690. });
  1691. it('Does not select a file with Ctrl or Shift if selection is not allowed', function() {
  1692. var $tr = fileList.findFileEl('One.txt');
  1693. var $tr2 = fileList.findFileEl('Three.pdf');
  1694. var e;
  1695. e = new $.Event('click');
  1696. e.ctrlKey = true;
  1697. $tr.find('td.filename .name').trigger(e);
  1698. // click on second entry, does not clear the selection
  1699. e = new $.Event('click');
  1700. e.ctrlKey = true;
  1701. $tr2.find('td.filename .name').trigger(e);
  1702. expect(fileList.getSelectedFiles().length).toEqual(0);
  1703. // deselect now
  1704. e = new $.Event('click');
  1705. e.shiftKey = true;
  1706. $tr2.find('td.filename .name').trigger(e);
  1707. expect(fileList.getSelectedFiles().length).toEqual(0);
  1708. });
  1709. })
  1710. });
  1711. describe('File actions', function() {
  1712. it('Clicking on a file name will trigger default action', function() {
  1713. var actionStub = sinon.stub();
  1714. fileList.setFiles(testFiles);
  1715. fileList.fileActions.register(
  1716. 'text/plain',
  1717. 'Test',
  1718. OC.PERMISSION_ALL,
  1719. function() {
  1720. // Specify icon for hitory button
  1721. return OC.imagePath('core','actions/history');
  1722. },
  1723. actionStub
  1724. );
  1725. fileList.fileActions.setDefault('text/plain', 'Test');
  1726. var $tr = fileList.findFileEl('One.txt');
  1727. $tr.find('td.filename>a.name').click();
  1728. expect(actionStub.calledOnce).toEqual(true);
  1729. expect(actionStub.getCall(0).args[0]).toEqual('One.txt');
  1730. var context = actionStub.getCall(0).args[1];
  1731. expect(context.$file.is($tr)).toEqual(true);
  1732. expect(context.fileList).toBeDefined();
  1733. expect(context.fileActions).toBeDefined();
  1734. expect(context.dir).toEqual('/subdir');
  1735. });
  1736. it('redisplays actions when new actions have been registered', function() {
  1737. var actionStub = sinon.stub();
  1738. var readyHandler = sinon.stub();
  1739. var clock = sinon.useFakeTimers();
  1740. var debounceStub = sinon.stub(_, 'debounce', function(callback) {
  1741. return function() {
  1742. // defer instead of debounce, to make it work with clock
  1743. _.defer(callback);
  1744. };
  1745. });
  1746. // need to reinit the list to make the debounce call
  1747. fileList.destroy();
  1748. fileList = new OCA.Files.FileList($('#app-content-files'));
  1749. fileList.setFiles(testFiles);
  1750. fileList.$fileList.on('fileActionsReady', readyHandler);
  1751. fileList.fileActions.register(
  1752. 'text/plain',
  1753. 'Test',
  1754. OC.PERMISSION_ALL,
  1755. function() {
  1756. // Specify icon for hitory button
  1757. return OC.imagePath('core','actions/history');
  1758. },
  1759. actionStub
  1760. );
  1761. var $tr = fileList.findFileEl('One.txt');
  1762. expect($tr.find('.action-test').length).toEqual(0);
  1763. expect(readyHandler.notCalled).toEqual(true);
  1764. // update is delayed
  1765. clock.tick(100);
  1766. expect($tr.find('.action-test').length).toEqual(1);
  1767. expect(readyHandler.calledOnce).toEqual(true);
  1768. clock.restore();
  1769. debounceStub.restore();
  1770. });
  1771. });
  1772. describe('Sorting files', function() {
  1773. it('Sorts by name by default', function() {
  1774. fileList.reload();
  1775. expect(fakeServer.requests.length).toEqual(1);
  1776. var url = fakeServer.requests[0].url;
  1777. var query = OC.parseQueryString(url.substr(url.indexOf('?') + 1));
  1778. expect(query.sort).toEqual('name');
  1779. expect(query.sortdirection).toEqual('asc');
  1780. });
  1781. it('Reloads file list with a different sort when clicking on column header of unsorted column', function() {
  1782. fileList.$el.find('.column-size .columntitle').click();
  1783. expect(fakeServer.requests.length).toEqual(1);
  1784. var url = fakeServer.requests[0].url;
  1785. var query = OC.parseQueryString(url.substr(url.indexOf('?') + 1));
  1786. expect(query.sort).toEqual('size');
  1787. expect(query.sortdirection).toEqual('desc');
  1788. });
  1789. it('Toggles sort direction when clicking on already sorted column', function() {
  1790. fileList.$el.find('.column-name .columntitle').click();
  1791. expect(fakeServer.requests.length).toEqual(1);
  1792. var url = fakeServer.requests[0].url;
  1793. var query = OC.parseQueryString(url.substr(url.indexOf('?') + 1));
  1794. expect(query.sort).toEqual('name');
  1795. expect(query.sortdirection).toEqual('desc');
  1796. });
  1797. it('Toggles the sort indicator when clicking on a column header', function() {
  1798. var ASC_CLASS = fileList.SORT_INDICATOR_ASC_CLASS;
  1799. var DESC_CLASS = fileList.SORT_INDICATOR_DESC_CLASS;
  1800. fileList.$el.find('.column-size .columntitle').click();
  1801. // moves triangle to size column, check indicator on name is hidden
  1802. expect(
  1803. fileList.$el.find('.column-name .sort-indicator').hasClass('hidden')
  1804. ).toEqual(true);
  1805. // check indicator on size is visible and defaults to descending
  1806. expect(
  1807. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  1808. ).toEqual(false);
  1809. expect(
  1810. fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
  1811. ).toEqual(true);
  1812. // click again on size column, reverses direction
  1813. fileList.$el.find('.column-size .columntitle').click();
  1814. expect(
  1815. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  1816. ).toEqual(false);
  1817. expect(
  1818. fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS)
  1819. ).toEqual(true);
  1820. // click again on size column, reverses direction
  1821. fileList.$el.find('.column-size .columntitle').click();
  1822. expect(
  1823. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  1824. ).toEqual(false);
  1825. expect(
  1826. fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
  1827. ).toEqual(true);
  1828. // click on mtime column, moves indicator there
  1829. fileList.$el.find('.column-mtime .columntitle').click();
  1830. expect(
  1831. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  1832. ).toEqual(true);
  1833. expect(
  1834. fileList.$el.find('.column-mtime .sort-indicator').hasClass('hidden')
  1835. ).toEqual(false);
  1836. expect(
  1837. fileList.$el.find('.column-mtime .sort-indicator').hasClass(DESC_CLASS)
  1838. ).toEqual(true);
  1839. });
  1840. it('Uses correct sort comparator when inserting files', function() {
  1841. testFiles.sort(OCA.Files.FileList.Comparators.size);
  1842. testFiles.reverse(); //default is descending
  1843. // this will make it reload the testFiles with the correct sorting
  1844. fileList.$el.find('.column-size .columntitle').click();
  1845. expect(fakeServer.requests.length).toEqual(1);
  1846. fakeServer.requests[0].respond(
  1847. 200,
  1848. { 'Content-Type': 'application/json' },
  1849. JSON.stringify({
  1850. status: 'success',
  1851. data: {
  1852. files: testFiles,
  1853. permissions: 31
  1854. }
  1855. })
  1856. );
  1857. var newFileData = {
  1858. id: 999,
  1859. type: 'file',
  1860. name: 'new file.txt',
  1861. mimetype: 'text/plain',
  1862. size: 40001,
  1863. etag: '999'
  1864. };
  1865. fileList.add(newFileData);
  1866. expect(fileList.findFileEl('Three.pdf').index()).toEqual(0);
  1867. expect(fileList.findFileEl('new file.txt').index()).toEqual(1);
  1868. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  1869. expect(fileList.findFileEl('somedir').index()).toEqual(3);
  1870. expect(fileList.findFileEl('One.txt').index()).toEqual(4);
  1871. expect(fileList.files.length).toEqual(5);
  1872. expect(fileList.$fileList.find('tr').length).toEqual(5);
  1873. });
  1874. it('Uses correct reversed sort comparator when inserting files', function() {
  1875. testFiles.sort(OCA.Files.FileList.Comparators.size);
  1876. // this will make it reload the testFiles with the correct sorting
  1877. fileList.$el.find('.column-size .columntitle').click();
  1878. expect(fakeServer.requests.length).toEqual(1);
  1879. fakeServer.requests[0].respond(
  1880. 200,
  1881. { 'Content-Type': 'application/json' },
  1882. JSON.stringify({
  1883. status: 'success',
  1884. data: {
  1885. files: testFiles,
  1886. permissions: 31
  1887. }
  1888. })
  1889. );
  1890. // reverse sort
  1891. fileList.$el.find('.column-size .columntitle').click();
  1892. fakeServer.requests[1].respond(
  1893. 200,
  1894. { 'Content-Type': 'application/json' },
  1895. JSON.stringify({
  1896. status: 'success',
  1897. data: {
  1898. files: testFiles,
  1899. permissions: 31
  1900. }
  1901. })
  1902. );
  1903. var newFileData = {
  1904. id: 999,
  1905. type: 'file',
  1906. name: 'new file.txt',
  1907. mimetype: 'text/plain',
  1908. size: 40001,
  1909. etag: '999'
  1910. };
  1911. fileList.add(newFileData);
  1912. expect(fileList.findFileEl('One.txt').index()).toEqual(0);
  1913. expect(fileList.findFileEl('somedir').index()).toEqual(1);
  1914. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  1915. expect(fileList.findFileEl('new file.txt').index()).toEqual(3);
  1916. expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
  1917. expect(fileList.files.length).toEqual(5);
  1918. expect(fileList.$fileList.find('tr').length).toEqual(5);
  1919. });
  1920. });
  1921. /**
  1922. * Test upload mostly by testing the code inside the event handlers
  1923. * that were registered on the magic upload object
  1924. */
  1925. describe('file upload', function() {
  1926. var $uploader;
  1927. beforeEach(function() {
  1928. // note: this isn't the real blueimp file uploader from jquery.fileupload
  1929. // but it makes it possible to simulate the event triggering to
  1930. // test the response of the handlers
  1931. $uploader = $('#file_upload_start');
  1932. fileList.setFiles(testFiles);
  1933. });
  1934. afterEach(function() {
  1935. $uploader = null;
  1936. });
  1937. describe('dropping external files', function() {
  1938. var uploadData;
  1939. /**
  1940. * Simulate drop event on the given target
  1941. *
  1942. * @param $target target element to drop on
  1943. * @return event object including the result
  1944. */
  1945. function dropOn($target, data) {
  1946. var eventData = {
  1947. originalEvent: {
  1948. target: $target
  1949. }
  1950. };
  1951. var ev = new $.Event('fileuploaddrop', eventData);
  1952. // using triggerHandler instead of trigger so we can pass
  1953. // extra data
  1954. $uploader.triggerHandler(ev, data || {});
  1955. return ev;
  1956. }
  1957. beforeEach(function() {
  1958. // simulate data structure from jquery.upload
  1959. uploadData = {
  1960. files: [{
  1961. relativePath: 'fileToUpload.txt'
  1962. }]
  1963. };
  1964. });
  1965. afterEach(function() {
  1966. uploadData = null;
  1967. });
  1968. it('drop on a tr or crumb outside file list does not trigger upload', function() {
  1969. var $anotherTable = $('<table><tbody><tr><td>outside<div class="crumb">crumb</div></td></tr></table>');
  1970. var ev;
  1971. $('#testArea').append($anotherTable);
  1972. ev = dropOn($anotherTable.find('tr'), uploadData);
  1973. expect(ev.result).toEqual(false);
  1974. ev = dropOn($anotherTable.find('.crumb'));
  1975. expect(ev.result).toEqual(false);
  1976. });
  1977. it('drop on an element outside file list container does not trigger upload', function() {
  1978. var $anotherEl = $('<div>outside</div>');
  1979. var ev;
  1980. $('#testArea').append($anotherEl);
  1981. ev = dropOn($anotherEl);
  1982. expect(ev.result).toEqual(false);
  1983. });
  1984. it('drop on an element inside the table triggers upload', function() {
  1985. var ev;
  1986. ev = dropOn(fileList.$fileList.find('th:first'), uploadData);
  1987. expect(ev.result).not.toEqual(false);
  1988. });
  1989. it('drop on an element on the table container triggers upload', function() {
  1990. var ev;
  1991. ev = dropOn($('#app-content-files'), uploadData);
  1992. expect(ev.result).not.toEqual(false);
  1993. });
  1994. it('drop on an element inside the table does not trigger upload if no upload permission', function() {
  1995. $('#permissions').val(0);
  1996. var ev;
  1997. ev = dropOn(fileList.$fileList.find('th:first'));
  1998. expect(ev.result).toEqual(false);
  1999. expect(notificationStub.calledOnce).toEqual(true);
  2000. });
  2001. it('drop on an folder does not trigger upload if no upload permission on that folder', function() {
  2002. var $tr = fileList.findFileEl('somedir');
  2003. var ev;
  2004. $tr.data('permissions', OC.PERMISSION_READ);
  2005. ev = dropOn($tr);
  2006. expect(ev.result).toEqual(false);
  2007. expect(notificationStub.calledOnce).toEqual(true);
  2008. });
  2009. it('drop on a file row inside the table triggers upload to current folder', function() {
  2010. var ev;
  2011. ev = dropOn(fileList.findFileEl('One.txt').find('td:first'), uploadData);
  2012. expect(ev.result).not.toEqual(false);
  2013. });
  2014. it('drop on a folder row inside the table triggers upload to target folder', function() {
  2015. var ev, formData;
  2016. ev = dropOn(fileList.findFileEl('somedir').find('td:eq(2)'), uploadData);
  2017. expect(ev.result).not.toEqual(false);
  2018. expect(uploadData.targetDir).toEqual('/subdir/somedir');
  2019. });
  2020. it('drop on a breadcrumb inside the table triggers upload to target folder', function() {
  2021. var ev, formData;
  2022. fileList.changeDirectory('a/b/c/d');
  2023. ev = dropOn(fileList.$el.find('.crumb:eq(2)'), uploadData);
  2024. expect(ev.result).not.toEqual(false);
  2025. expect(uploadData.targetDir).toEqual('/a/b');
  2026. });
  2027. });
  2028. });
  2029. describe('Handeling errors', function () {
  2030. beforeEach(function () {
  2031. redirectStub = sinon.stub(OC, 'redirect');
  2032. fileList = new OCA.Files.FileList($('#app-content-files'));
  2033. });
  2034. afterEach(function () {
  2035. fileList = undefined;
  2036. redirectStub.restore();
  2037. });
  2038. it('reloads the page on authentication errors', function () {
  2039. fileList.reload();
  2040. fakeServer.requests[0].respond(
  2041. 200,
  2042. { 'Content-Type': 'application/json' },
  2043. JSON.stringify({
  2044. status: 'error',
  2045. data: {
  2046. 'error': 'authentication_error'
  2047. }
  2048. })
  2049. );
  2050. expect(redirectStub.calledWith(OC.generateUrl('apps/files'))).toEqual(true);
  2051. });
  2052. });
  2053. });