filelistSpec.js 124 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405
  1. /**
  2. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. describe('OCA.Files.FileList tests', function() {
  7. var FileInfo = OC.Files.FileInfo;
  8. var testFiles, testRoot, notificationStub, fileList, pageSizeStub;
  9. var bcResizeStub;
  10. var filesClient;
  11. var filesConfig;
  12. var redirectStub;
  13. /**
  14. * Generate test file data
  15. */
  16. function generateFiles(startIndex, endIndex) {
  17. var files = [];
  18. var name;
  19. for (var i = startIndex; i <= endIndex; i++) {
  20. name = 'File with index ';
  21. if (i < 10) {
  22. // do not rely on localeCompare here
  23. // and make the sorting predictable
  24. // cross-browser
  25. name += '0';
  26. }
  27. name += i + '.txt';
  28. files.push(new FileInfo({
  29. id: i,
  30. type: 'file',
  31. name: name,
  32. mimetype: 'text/plain',
  33. size: i * 2,
  34. etag: 'abc'
  35. }));
  36. }
  37. return files;
  38. }
  39. beforeEach(function() {
  40. filesConfig = new OC.Backbone.Model({
  41. showhidden: true
  42. });
  43. filesClient = new OC.Files.Client({
  44. host: 'localhost',
  45. port: 80,
  46. // FIXME: uncomment after fixing the test OC.getRootPath()
  47. //root: OC.getRootPath() + '/remote.php/webdav',
  48. root: '/remote.php/webdav',
  49. useHTTPS: false
  50. });
  51. redirectStub = sinon.stub(OC, 'redirect');
  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="permissions" value="31"/>' +
  61. // dummy controls
  62. '<div class="files-controls">' +
  63. ' <div class="actions creatable"></div>' +
  64. ' <div class="notCreatable"></div>' +
  65. '</div>' +
  66. // uploader
  67. '<input type="file" id="file_upload_start" name="files[]" multiple="multiple">' +
  68. // dummy table
  69. // TODO: at some point this will be rendered by the fileList class itself!
  70. '<table class="files-filestable list-container view-grid">' +
  71. '<thead><tr>' +
  72. '<th class="hidden column-name">' +
  73. '<input type="checkbox" id="select_all_files" class="select-all checkbox">' +
  74. '<a class="name columntitle" href="#" onclick="event.preventDefault()" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
  75. '<span class="selectedActions hidden">' +
  76. '<a class="actions-selected" href="#" onclick="event.preventDefault()"><span class="icon icon-more"></span><span>Actions</span></a>' +
  77. '</th>' +
  78. '<th class="hidden column-size"><a class="columntitle" href="#" onclick="event.preventDefault()" data-sort="size"><span class="sort-indicator"></span></a></th>' +
  79. '<th class="hidden column-mtime"><a class="columntitle" href="#" onclick="event.preventDefault()" data-sort="mtime"><span class="sort-indicator"></span></a></th>' +
  80. '</tr></thead>' +
  81. '<tbody class="files-fileList"></tbody>' +
  82. '<tfoot></tfoot>' +
  83. '</table>' +
  84. // TODO: move to handlebars template
  85. '<div class="emptyfilelist emptycontent"><h2>Empty content message</h2><p class="uploadmessage">Upload message</p></div>' +
  86. '<div class="nofilterresults hidden"></div>' +
  87. '</div>'
  88. );
  89. testRoot = new FileInfo({
  90. // root entry
  91. id: 99,
  92. type: 'dir',
  93. name: '/subdir',
  94. mimetype: 'httpd/unix-directory',
  95. size: 1200000,
  96. etag: 'a0b0c0d0',
  97. permissions: OC.PERMISSION_ALL
  98. });
  99. testFiles = [new FileInfo({
  100. id: 1,
  101. type: 'file',
  102. name: 'One.txt',
  103. mimetype: 'text/plain',
  104. mtime: 123456789,
  105. size: 12,
  106. etag: 'abc',
  107. permissions: OC.PERMISSION_ALL
  108. }), new FileInfo({
  109. id: 2,
  110. type: 'file',
  111. name: 'Two.jpg',
  112. mimetype: 'image/jpeg',
  113. mtime: 234567890,
  114. size: 12049,
  115. etag: 'def',
  116. permissions: OC.PERMISSION_ALL
  117. }), new FileInfo({
  118. id: 3,
  119. type: 'file',
  120. name: 'Three.pdf',
  121. mimetype: 'application/pdf',
  122. mtime: 234560000,
  123. size: 58009,
  124. etag: '123',
  125. permissions: OC.PERMISSION_ALL
  126. }), new FileInfo({
  127. id: 4,
  128. type: 'dir',
  129. name: 'somedir',
  130. mimetype: 'httpd/unix-directory',
  131. mtime: 134560000,
  132. size: 250,
  133. etag: '456',
  134. permissions: OC.PERMISSION_ALL
  135. })];
  136. pageSizeStub = sinon.stub(OCA.Files.FileList.prototype, 'pageSize').returns(20);
  137. fileList = new OCA.Files.FileList($('#app-content-files'), {
  138. filesClient: filesClient,
  139. config: filesConfig,
  140. dir: '/subdir',
  141. enableUpload: true,
  142. multiSelectMenu: [{
  143. name: 'copyMove',
  144. displayName: t('files', 'Move or copy'),
  145. iconClass: 'icon-external',
  146. },
  147. {
  148. name: 'download',
  149. displayName: t('files', 'Download'),
  150. iconClass: 'icon-download',
  151. },
  152. {
  153. name: 'delete',
  154. displayName: t('files', 'Delete'),
  155. iconClass: 'icon-delete',
  156. }]
  157. });
  158. });
  159. afterEach(function() {
  160. testFiles = undefined;
  161. if (fileList) {
  162. fileList.destroy();
  163. }
  164. fileList = undefined;
  165. notificationStub.restore();
  166. bcResizeStub.restore();
  167. pageSizeStub.restore();
  168. redirectStub.restore();
  169. });
  170. describe('Getters', function() {
  171. it('Returns the current directory', function() {
  172. fileList.changeDirectory('/one/two/three', false, true);
  173. expect(fileList.getCurrentDirectory()).toEqual('/one/two/three');
  174. });
  175. it('Returns the directory permissions as int', function() {
  176. $('#permissions').val('23');
  177. expect(fileList.getDirectoryPermissions()).toEqual(23);
  178. });
  179. });
  180. describe('Adding files', function() {
  181. it('generates file element with correct attributes when calling add() with file data', function() {
  182. var fileData = new FileInfo({
  183. id: 18,
  184. name: 'testName.txt',
  185. mimetype: 'text/plain',
  186. size: 1234,
  187. etag: 'a01234c',
  188. mtime: 123456
  189. });
  190. var $tr = fileList.add(fileData);
  191. expect($tr).toBeDefined();
  192. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  193. expect($tr.attr('data-id')).toEqual('18');
  194. expect($tr.attr('data-type')).toEqual('file');
  195. expect($tr.attr('data-file')).toEqual('testName.txt');
  196. expect($tr.attr('data-size')).toEqual('1234');
  197. expect($tr.attr('data-etag')).toEqual('a01234c');
  198. expect($tr.attr('data-permissions')).toEqual('31');
  199. expect($tr.attr('data-mime')).toEqual('text/plain');
  200. expect($tr.attr('data-mtime')).toEqual('123456');
  201. expect($tr.attr('data-e2eencrypted')).toEqual('false');
  202. expect($tr.find('a.name').attr('href'))
  203. .toEqual(OC.getRootPath() + '/remote.php/webdav/subdir/testName.txt');
  204. expect($tr.find('.nametext').text().trim()).toEqual('testName.txt');
  205. expect($tr.find('.filesize').text()).toEqual('1 KB');
  206. expect($tr.find('.date').text()).not.toEqual('?');
  207. expect(fileList.findFileEl('testName.txt')[0]).toEqual($tr[0]);
  208. });
  209. it('generates file element with url for default action when one is defined', function() {
  210. var actionStub = sinon.stub();
  211. fileList.setFiles(testFiles);
  212. fileList.fileActions.registerAction({
  213. mime: 'text/plain',
  214. name: 'Test',
  215. type: OCA.Files.FileActions.TYPE_INLINE,
  216. permissions: OC.PERMISSION_ALL,
  217. icon: function() {
  218. // Specify icon for history button
  219. return OC.imagePath('core','actions/history');
  220. },
  221. actionHandler: actionStub
  222. });
  223. fileList.fileActions.setDefault('text/plain', 'Test');
  224. var fileData = new FileInfo({
  225. id: 18,
  226. name: 'testName.txt',
  227. mimetype: 'text/plain',
  228. size: 1234,
  229. etag: 'a01234c',
  230. mtime: 123456
  231. });
  232. var $tr = fileList.add(fileData);
  233. expect($tr.find('a.name').attr('href'))
  234. .toEqual(OC.getRootPath() + '/index.php/apps/files?dir=&openfile=18');
  235. });
  236. it('generates dir element with correct attributes when calling add() with dir data', function() {
  237. var fileData = new FileInfo({
  238. id: 19,
  239. name: 'testFolder',
  240. mimetype: 'httpd/unix-directory',
  241. size: 1234,
  242. etag: 'a01234c',
  243. mtime: 123456
  244. });
  245. var $tr = fileList.add(fileData);
  246. expect($tr).toBeDefined();
  247. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  248. expect($tr.attr('data-id')).toEqual('19');
  249. expect($tr.attr('data-type')).toEqual('dir');
  250. expect($tr.attr('data-file')).toEqual('testFolder');
  251. expect($tr.attr('data-size')).toEqual('1234');
  252. expect($tr.attr('data-etag')).toEqual('a01234c');
  253. expect($tr.attr('data-permissions')).toEqual('31');
  254. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  255. expect($tr.attr('data-mtime')).toEqual('123456');
  256. expect($tr.attr('data-e2eencrypted')).toEqual('false');
  257. expect($tr.find('.filesize').text()).toEqual('1 KB');
  258. expect($tr.find('.date').text()).not.toEqual('?');
  259. expect(fileList.findFileEl('testFolder')[0]).toEqual($tr[0]);
  260. });
  261. it('generates file element with default attributes when calling add() with minimal data', function() {
  262. var fileData = {
  263. type: 'file',
  264. name: 'testFile.txt'
  265. };
  266. var $tr = fileList.add(fileData);
  267. expect($tr).toBeDefined();
  268. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  269. expect($tr.attr('data-id')).toBeUndefined();
  270. expect($tr.attr('data-type')).toEqual('file');
  271. expect($tr.attr('data-file')).toEqual('testFile.txt');
  272. expect($tr.attr('data-size')).toBeUndefined();
  273. expect($tr.attr('data-etag')).toBeUndefined();
  274. expect($tr.attr('data-permissions')).toEqual('31');
  275. expect($tr.attr('data-mime')).toBeUndefined();
  276. expect($tr.attr('data-e2eencrypted')).toEqual('false');
  277. expect($tr.find('.filesize').text()).toEqual('Pending');
  278. expect($tr.find('.date').text()).not.toEqual('?');
  279. });
  280. it('generates dir element with default attributes when calling add() with minimal data', function() {
  281. var fileData = {
  282. type: 'dir',
  283. name: 'testFolder'
  284. };
  285. var $tr = fileList.add(fileData);
  286. expect($tr).toBeDefined();
  287. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  288. expect($tr.attr('data-id')).toBeUndefined();
  289. expect($tr.attr('data-type')).toEqual('dir');
  290. expect($tr.attr('data-file')).toEqual('testFolder');
  291. expect($tr.attr('data-size')).toBeUndefined();
  292. expect($tr.attr('data-etag')).toBeUndefined();
  293. expect($tr.attr('data-permissions')).toEqual('31');
  294. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  295. expect($tr.attr('data-e2eencrypted')).toEqual('false');
  296. expect($tr.find('.filesize').text()).toEqual('Pending');
  297. expect($tr.find('.date').text()).not.toEqual('?');
  298. });
  299. it('generates dir element with true e2eencrypted attribute when calling add() with minimal data including isEncrypted', function() {
  300. var fileData = {
  301. type: 'dir',
  302. name: 'testFolder',
  303. isEncrypted: true
  304. };
  305. var $tr = fileList.add(fileData);
  306. expect($tr.attr('data-e2eencrypted')).toEqual('true');
  307. });
  308. it('generates file element with no permissions when permissions are explicitly none', function() {
  309. var fileData = {
  310. type: 'dir',
  311. name: 'testFolder',
  312. permissions: OC.PERMISSION_NONE
  313. };
  314. var $tr = fileList.add(fileData);
  315. expect($tr.attr('data-permissions')).toEqual('0');
  316. });
  317. it('generates file element with zero size when size is explicitly zero', function() {
  318. var fileData = {
  319. type: 'dir',
  320. name: 'testFolder',
  321. size: '0'
  322. };
  323. var $tr = fileList.add(fileData);
  324. expect($tr.find('.filesize').text()).toEqual('0 KB');
  325. });
  326. it('generates file element with unknown date when mtime invalid', function() {
  327. var fileData = {
  328. type: 'dir',
  329. name: 'testFolder',
  330. mtime: -1
  331. };
  332. var $tr = fileList.add(fileData);
  333. expect($tr.find('.date .modified').text()).toEqual('?');
  334. });
  335. it('adds new file to the end of the list', function() {
  336. var $tr;
  337. var fileData = {
  338. type: 'file',
  339. name: 'ZZZ.txt'
  340. };
  341. fileList.setFiles(testFiles);
  342. $tr = fileList.add(fileData);
  343. expect($tr.index()).toEqual(4);
  344. });
  345. it('inserts files in a sorted manner when insert option is enabled', function() {
  346. for (var i = 0; i < testFiles.length; i++) {
  347. fileList.add(testFiles[i]);
  348. }
  349. expect(fileList.files[0].name).toEqual('somedir');
  350. expect(fileList.files[1].name).toEqual('One.txt');
  351. expect(fileList.files[2].name).toEqual('Three.pdf');
  352. expect(fileList.files[3].name).toEqual('Two.jpg');
  353. });
  354. it('inserts new file at correct position', function() {
  355. var $tr;
  356. var fileData = {
  357. type: 'file',
  358. name: 'P comes after O.txt'
  359. };
  360. for (var i = 0; i < testFiles.length; i++) {
  361. fileList.add(testFiles[i]);
  362. }
  363. $tr = fileList.add(fileData);
  364. // after "One.txt"
  365. expect($tr.index()).toEqual(2);
  366. expect(fileList.files[2]).toEqual(fileData);
  367. });
  368. it('inserts new folder at correct position in insert mode', function() {
  369. var $tr;
  370. var fileData = {
  371. type: 'dir',
  372. name: 'somedir2 comes after somedir'
  373. };
  374. for (var i = 0; i < testFiles.length; i++) {
  375. fileList.add(testFiles[i]);
  376. }
  377. $tr = fileList.add(fileData);
  378. expect($tr.index()).toEqual(1);
  379. expect(fileList.files[1]).toEqual(fileData);
  380. });
  381. it('inserts new file at the end correctly', function() {
  382. var $tr;
  383. var fileData = {
  384. type: 'file',
  385. name: 'zzz.txt'
  386. };
  387. for (var i = 0; i < testFiles.length; i++) {
  388. fileList.add(testFiles[i]);
  389. }
  390. $tr = fileList.add(fileData);
  391. expect($tr.index()).toEqual(4);
  392. expect(fileList.files[4]).toEqual(fileData);
  393. });
  394. it('removes empty content message and shows summary when adding first file', function() {
  395. var $summary;
  396. var fileData = {
  397. type: 'file',
  398. name: 'first file.txt',
  399. size: 12
  400. };
  401. fileList.setFiles([]);
  402. expect(fileList.isEmpty).toEqual(true);
  403. fileList.add(fileData);
  404. $summary = $('.files-filestable .summary');
  405. expect($summary.hasClass('hidden')).toEqual(false);
  406. // yes, ugly...
  407. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  408. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  409. expect($summary.find('.connector').hasClass('hidden')).toEqual(true);
  410. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  411. expect($summary.find('.filesize').text()).toEqual('12 B');
  412. expect($('.files-filestable thead th').hasClass('hidden')).toEqual(false);
  413. expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(true);
  414. expect(fileList.isEmpty).toEqual(false);
  415. });
  416. it('correctly adds the extension markup and show hidden files completely in gray', function() {
  417. var $tr;
  418. var testDataAndExpectedResult = [
  419. {file: {type: 'file', name: 'ZZZ.txt'}, extension: '.txt'},
  420. {file: {type: 'file', name: 'ZZZ.tar.gz'}, extension: '.gz'},
  421. {file: {type: 'file', name: 'test.with.some.dots.in.it.txt'}, extension: '.txt'},
  422. // we render hidden files completely in gray
  423. {file: {type: 'file', name: '.test.with.some.dots.in.it.txt'}, extension: '.test.with.some.dots.in.it.txt'},
  424. {file: {type: 'file', name: '.hidden'}, extension: '.hidden'},
  425. ];
  426. fileList.setFiles(testFiles);
  427. for(var i = 0; i < testDataAndExpectedResult.length; i++) {
  428. var testSet = testDataAndExpectedResult[i];
  429. var fileData = testSet['file'];
  430. $tr = fileList.add(fileData);
  431. expect($tr.find('.nametext .extension').text()).toEqual(testSet['extension']);
  432. }
  433. });
  434. });
  435. describe('Hidden files', function() {
  436. it('sets the class hidden-file for hidden files', function() {
  437. var fileData = {
  438. type: 'dir',
  439. name: '.testFolder'
  440. };
  441. var $tr = fileList.add(fileData);
  442. expect($tr).toBeDefined();
  443. expect($tr.hasClass('hidden-file')).toEqual(true);
  444. });
  445. it('does not set the class hidden-file for visible files', function() {
  446. var fileData = {
  447. type: 'dir',
  448. name: 'testFolder'
  449. };
  450. var $tr = fileList.add(fileData);
  451. expect($tr).toBeDefined();
  452. expect($tr.hasClass('hidden-file')).toEqual(false);
  453. });
  454. it('toggles the list\'s class when toggling hidden files', function() {
  455. expect(fileList.$el.hasClass('hide-hidden-files')).toEqual(false);
  456. window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
  457. expect(fileList.$el.hasClass('hide-hidden-files')).toEqual(true);
  458. window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true })
  459. expect(fileList.$el.hasClass('hide-hidden-files')).toEqual(false);
  460. });
  461. });
  462. describe('Removing files from the list', function() {
  463. it('Removes file from list when calling remove() and updates summary', function() {
  464. var $summary;
  465. var $removedEl;
  466. fileList.setFiles(testFiles);
  467. $removedEl = fileList.remove('One.txt');
  468. expect($removedEl).toBeDefined();
  469. expect($removedEl.attr('data-file')).toEqual('One.txt');
  470. expect($('.files-fileList tr').length).toEqual(3);
  471. expect(fileList.files.length).toEqual(3);
  472. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  473. $summary = $('.files-filestable .summary');
  474. expect($summary.hasClass('hidden')).toEqual(false);
  475. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  476. expect($summary.find('.fileinfo').text()).toEqual('2 files');
  477. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
  478. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  479. expect($summary.find('.filesize').text()).toEqual('69 KB');
  480. expect(fileList.isEmpty).toEqual(false);
  481. });
  482. it('Shows empty content when removing last file', function() {
  483. var $summary;
  484. fileList.setFiles([testFiles[0]]);
  485. fileList.remove('One.txt');
  486. expect($('.files-fileList tr').length).toEqual(0);
  487. expect(fileList.files.length).toEqual(0);
  488. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  489. $summary = $('.files-filestable .summary');
  490. expect($summary.hasClass('hidden')).toEqual(true);
  491. expect($('.files-filestable thead th').hasClass('hidden')).toEqual(true);
  492. expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(false);
  493. expect(fileList.isEmpty).toEqual(true);
  494. });
  495. });
  496. describe('Deleting files', function() {
  497. var deferredDelete;
  498. var deleteStub;
  499. beforeEach(function() {
  500. deferredDelete = $.Deferred();
  501. deleteStub = sinon.stub(filesClient, 'remove');
  502. });
  503. afterEach(function() {
  504. deleteStub.restore();
  505. });
  506. function doDelete() {
  507. // note: normally called from FileActions
  508. return fileList.do_delete(['One.txt', 'Two.jpg']).then(function(){
  509. expect(deleteStub.calledTwice).toEqual(true);
  510. expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  511. expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Two.jpg');
  512. });
  513. }
  514. it('calls delete.php, removes the deleted entries and updates summary', function(done) {
  515. var $summary;
  516. fileList.setFiles(testFiles);
  517. deferredDelete1 = $.Deferred();
  518. deferredDelete2 = $.Deferred();
  519. deleteStub.onCall(0).callsFake(function(src){
  520. expect(deleteStub.calledOnce).toEqual(true);
  521. expect(src).toEqual('/subdir/One.txt');
  522. return deferredDelete1.promise();
  523. });
  524. deleteStub.onCall(1).callsFake(function(src){
  525. expect(deleteStub.calledTwice).toEqual(true);
  526. expect(src).toEqual('/subdir/Two.jpg');
  527. return deferredDelete2.promise();
  528. });
  529. var promise = fileList.do_delete(['One.txt', 'Two.jpg']);
  530. deferredDelete1.resolve(200);
  531. deferredDelete2.resolve(200);
  532. return promise.then(function(){
  533. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  534. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  535. expect(fileList.findFileEl('Three.pdf').length).toEqual(1);
  536. expect(fileList.$fileList.find('tr').length).toEqual(2);
  537. $summary = $('.files-filestable .summary');
  538. expect($summary.hasClass('hidden')).toEqual(false);
  539. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  540. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  541. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
  542. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  543. expect($summary.find('.filesize').text()).toEqual('57 KB');
  544. expect(fileList.isEmpty).toEqual(false);
  545. expect($('.files-filestable thead th').hasClass('hidden')).toEqual(false);
  546. expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(true);
  547. expect(notificationStub.notCalled).toEqual(true);
  548. }).then(done, done);
  549. });
  550. it('shows busy state on files to be deleted', function(done) {
  551. fileList.setFiles(testFiles);
  552. deferredDelete1 = $.Deferred();
  553. deferredDelete2 = $.Deferred();
  554. deleteStub.onCall(0).callsFake(function(src){
  555. expect(fileList.findFileEl('One.txt').hasClass('busy')).toEqual(true);
  556. expect(fileList.findFileEl('Three.pdf').hasClass('busy')).toEqual(false);
  557. expect(deleteStub.calledOnce).toEqual(true);
  558. expect(src).toEqual('/subdir/One.txt');
  559. return deferredDelete1.promise();
  560. });
  561. deleteStub.onCall(1).callsFake(function(src){
  562. expect(fileList.findFileEl('Two.jpg').hasClass('busy')).toEqual(true);
  563. expect(fileList.findFileEl('Three.pdf').hasClass('busy')).toEqual(false);
  564. expect(deleteStub.calledTwice).toEqual(true);
  565. expect(src).toEqual('/subdir/Two.jpg');
  566. return deferredDelete2.promise();
  567. });
  568. var promise = fileList.do_delete(['One.txt', 'Two.jpg']).then(function(){
  569. expect(deleteStub.calledTwice).toEqual(true);
  570. });
  571. deferredDelete1.resolve(200);
  572. deferredDelete2.resolve(200);
  573. return promise.then(function(){
  574. expect(fileList.findFileEl('One.txt').hasClass('busy')).toEqual(false);
  575. expect(fileList.findFileEl('Two.jpg').hasClass('busy')).toEqual(false);
  576. }).then(done, done);
  577. });
  578. it('shows busy state on all files when deleting all', function(done) {
  579. fileList.setFiles(testFiles);
  580. var deferredDeleteArray = [];
  581. var count = 0;
  582. for (var i = 0; i < 4; i++) {
  583. (function(i, fn){
  584. deferredDeleteArray.push($.Deferred());
  585. deleteStub.onCall(i).callsFake(function(src){
  586. expect(fileList.findFileEl(fn).hasClass('busy')).toEqual(true);
  587. count++;
  588. return deferredDeleteArray[i].promise();
  589. });
  590. })(i, testFiles[i].name);
  591. }
  592. var promise = fileList.do_delete();
  593. for (var i = 0; i < 4; i++) {
  594. deferredDeleteArray[i].resolve(200);
  595. }
  596. return promise.then(function(){
  597. expect(count).toEqual(4);
  598. }).then(done, done);
  599. });
  600. it('updates summary when deleting last file', function(done) {
  601. var $summary;
  602. fileList.setFiles([testFiles[0], testFiles[1]]);
  603. deleteStub.returns(deferredDelete.promise());
  604. deferredDelete.resolve(200);
  605. return doDelete().then(function(){
  606. expect(fileList.$fileList.find('tr').length).toEqual(0);
  607. $summary = $('.files-filestable .summary');
  608. expect($summary.hasClass('hidden')).toEqual(true);
  609. expect(fileList.isEmpty).toEqual(true);
  610. expect(fileList.files.length).toEqual(0);
  611. expect($('.files-filestable thead th').hasClass('hidden')).toEqual(true);
  612. expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(false);
  613. }).then(done, done);
  614. });
  615. it('bring back deleted item when delete call failed', function(done) {
  616. fileList.setFiles(testFiles);
  617. deleteStub.returns(deferredDelete.promise());
  618. var promise = doDelete();
  619. deferredDelete.reject(403);
  620. return promise.then(function(){
  621. // files are still in the list
  622. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  623. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  624. expect(fileList.$fileList.find('tr').length).toEqual(4);
  625. expect(notificationStub.calledTwice).toEqual(true);
  626. }).then(done, done);
  627. });
  628. it('remove file from list if delete call returned 404 not found', function(done) {
  629. fileList.setFiles(testFiles);
  630. deleteStub.returns(deferredDelete.promise());
  631. var promise = doDelete();
  632. deferredDelete.reject(404);
  633. return promise.then(function(){
  634. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  635. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  636. expect(fileList.$fileList.find('tr').length).toEqual(2);
  637. expect(notificationStub.notCalled).toEqual(true);
  638. }).then(done, done);
  639. });
  640. });
  641. describe('Renaming files', function() {
  642. var deferredRename;
  643. var renameStub;
  644. beforeEach(function() {
  645. deferredRename = $.Deferred();
  646. renameStub = sinon.stub(filesClient, 'move').returns(deferredRename.promise());
  647. for (var i = 0; i < testFiles.length; i++) {
  648. var file = testFiles[i];
  649. file.path = '/some/subdir';
  650. fileList.add(file, {silent: true});
  651. }
  652. });
  653. afterEach(function() {
  654. renameStub.restore();
  655. });
  656. function doCancelRename() {
  657. var $input;
  658. // trigger rename prompt
  659. fileList.rename('One.txt');
  660. $input = fileList.$fileList.find('input.filename');
  661. // keep same name
  662. $input.val('One.txt');
  663. // trigger submit because triggering blur doesn't work in all browsers
  664. $input.closest('form').trigger('submit');
  665. expect(renameStub.notCalled).toEqual(true);
  666. }
  667. function doRename() {
  668. var $input;
  669. // trigger rename prompt
  670. fileList.rename('One.txt');
  671. $input = fileList.$fileList.find('input.filename');
  672. $input.val('Tu_after_three.txt');
  673. // trigger submit because triggering blur doesn't work in all browsers
  674. $input.closest('form').trigger('submit');
  675. expect(renameStub.calledOnce).toEqual(true);
  676. expect(renameStub.getCall(0).args[0]).toEqual('/some/subdir/One.txt');
  677. expect(renameStub.getCall(0).args[1]).toEqual('/some/subdir/Tu_after_three.txt');
  678. }
  679. it('Inserts renamed file entry at correct position if rename ajax call suceeded', function() {
  680. doRename();
  681. deferredRename.resolve(201);
  682. // element stays renamed
  683. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  684. expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(1);
  685. expect(fileList.findFileEl('Tu_after_three.txt').index()).toEqual(2); // after Two.jpg
  686. expect(notificationStub.notCalled).toEqual(true);
  687. });
  688. it('Reverts file entry if rename ajax call failed', function() {
  689. doRename();
  690. deferredRename.reject(403);
  691. // element was reverted
  692. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  693. expect(fileList.findFileEl('One.txt').index()).toEqual(1); // after somedir
  694. expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(0);
  695. expect(notificationStub.calledOnce).toEqual(true);
  696. });
  697. it('Correctly updates file link after rename', function() {
  698. var $tr;
  699. doRename();
  700. deferredRename.resolve(201);
  701. $tr = fileList.findFileEl('Tu_after_three.txt');
  702. expect($tr.find('a.name').attr('href'))
  703. .toEqual(OC.getRootPath() + '/remote.php/webdav/some/subdir/Tu_after_three.txt');
  704. });
  705. it('Triggers "fileActionsReady" event after rename', function() {
  706. var handler = sinon.stub();
  707. fileList.$fileList.on('fileActionsReady', handler);
  708. doRename();
  709. expect(handler.notCalled).toEqual(true);
  710. deferredRename.resolve(201);
  711. expect(handler.calledOnce).toEqual(true);
  712. expect(fileList.$fileList.find('.test').length).toEqual(0);
  713. });
  714. it('Leaves the summary alone when reinserting renamed element', function() {
  715. var $summary = $('.files-filestable .summary');
  716. doRename();
  717. deferredRename.resolve(201);
  718. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  719. expect($summary.find('.fileinfo').text()).toEqual('3 files');
  720. });
  721. it('Leaves the summary alone when cancel renaming', function() {
  722. var $summary = $('.files-filestable .summary');
  723. doCancelRename();
  724. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  725. expect($summary.find('.fileinfo').text()).toEqual('3 files');
  726. });
  727. it('Shows busy state while rename in progress', function() {
  728. var $tr;
  729. doRename();
  730. // element is renamed before the request finishes
  731. $tr = fileList.findFileEl('Tu_after_three.txt');
  732. expect($tr.length).toEqual(1);
  733. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  734. // file actions are hidden
  735. expect($tr.hasClass('busy')).toEqual(true);
  736. // input and form are gone
  737. expect(fileList.$fileList.find('input.filename').length).toEqual(0);
  738. expect(fileList.$fileList.find('form').length).toEqual(0);
  739. });
  740. it('Validates the file name', function() {
  741. var $input, $tr;
  742. $tr = fileList.findFileEl('One.txt');
  743. expect($tr.find('a.name').css('display')).not.toEqual('none');
  744. // trigger rename prompt
  745. fileList.rename('One.txt');
  746. expect($tr.find('a.name .thumbnail-wrapper').css('display')).not.toEqual('none');
  747. expect($tr.find('a.name .nametext').css('display')).toEqual('none');
  748. $input = fileList.$fileList.find('input.filename');
  749. $input.val('Two.jpg');
  750. // simulate key to trigger validation
  751. $input.trigger(new $.Event('keyup', {keyCode: 97}));
  752. // input is still there with error
  753. expect(fileList.$fileList.find('input.filename').length).toEqual(1);
  754. expect(fileList.$fileList.find('input.filename').hasClass('error')).toEqual(true);
  755. // trigger submit does not send server request
  756. $input.closest('form').trigger('submit');
  757. expect(renameStub.notCalled).toEqual(true);
  758. // simulate escape key
  759. $input.trigger(new $.Event('keyup', {keyCode: 27}));
  760. // element is added back with the correct name
  761. $tr = fileList.findFileEl('One.txt');
  762. expect($tr.length).toEqual(1);
  763. expect($tr.find('a .nametext').text().trim()).toEqual('One.txt');
  764. expect($tr.find('a.name').css('display')).not.toEqual('none');
  765. $tr = fileList.findFileEl('Two.jpg');
  766. expect($tr.length).toEqual(1);
  767. expect($tr.find('a .nametext').text().trim()).toEqual('Two.jpg');
  768. expect($tr.find('a.name').css('display')).not.toEqual('none');
  769. // input and form are gone
  770. expect(fileList.$fileList.find('input.filename').length).toEqual(0);
  771. expect(fileList.$fileList.find('form').length).toEqual(0);
  772. });
  773. it('Restores thumbnail when rename was cancelled', function(done) {
  774. doRename();
  775. expect(fileList.findFileEl('Tu_after_three.txt').find('.thumbnail').parent().attr('class'))
  776. .toContain('icon-loading-small');
  777. deferredRename.reject(409);
  778. return Promise.resolve().then(function() {
  779. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  780. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  781. .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
  782. }).then(done, done);
  783. });
  784. });
  785. describe('Moving files', function() {
  786. var deferredMove;
  787. var moveStub;
  788. beforeEach(function() {
  789. deferredMove = $.Deferred();
  790. moveStub = sinon.stub(filesClient, 'move');
  791. fileList.setFiles(testFiles);
  792. });
  793. afterEach(function() {
  794. moveStub.restore();
  795. });
  796. it('Moves single file to target folder', function(done) {
  797. var promise = fileList.move('One.txt', '/somedir');
  798. moveStub.callsFake(function(src, dst){
  799. expect(moveStub.calledOnce).toEqual(true);
  800. expect(src).toEqual('/subdir/One.txt');
  801. expect(dst).toEqual('/somedir/One.txt');
  802. return deferredMove.promise();
  803. });
  804. deferredMove.resolve(201);
  805. return promise.then(function(){
  806. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  807. // folder size has increased
  808. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  809. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  810. expect(notificationStub.notCalled).toEqual(true);
  811. }).then(done, done);
  812. });
  813. it('Moves list of files to target folder', function(done) {
  814. var deferredMove1 = $.Deferred();
  815. var deferredMove2 = $.Deferred();
  816. moveStub.onCall(0).callsFake(function(src, dst){
  817. expect(moveStub.calledOnce).toEqual(true);
  818. expect(src).toEqual('/subdir/One.txt');
  819. expect(dst).toEqual('/somedir/One.txt');
  820. return deferredMove1.promise();
  821. });
  822. moveStub.onCall(1).callsFake(function(src, dst){
  823. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  824. // folder size has increased during move
  825. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  826. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  827. expect(src).toEqual('/subdir/Two.jpg');
  828. expect(dst).toEqual('/somedir/Two.jpg');
  829. return deferredMove2.promise();
  830. });
  831. var promise = fileList.move(['One.txt', 'Two.jpg'], '/somedir');
  832. deferredMove1.resolve(201);
  833. deferredMove2.resolve(201);
  834. return promise.then(function(){
  835. expect(moveStub.calledTwice).toEqual(true);
  836. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  837. // folder size has increased
  838. expect(fileList.findFileEl('somedir').data('size')).toEqual(12311);
  839. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 KB');
  840. expect(notificationStub.notCalled).toEqual(true);
  841. }).then(done, done);
  842. });
  843. it('Shows notification if a file could not be moved', function(done) {
  844. moveStub.callsFake(function(){
  845. expect(moveStub.calledOnce).toEqual(true);
  846. return deferredMove.promise();
  847. });
  848. var promise = fileList.move('One.txt', '/somedir');
  849. deferredMove.reject(409);
  850. return promise.then(function(){
  851. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  852. expect(notificationStub.calledOnce).toEqual(true);
  853. expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
  854. }).then(done, done);
  855. });
  856. it('Restores thumbnail if a file could not be moved', function(done) {
  857. moveStub.callsFake(function(){
  858. expect(fileList.findFileEl('One.txt').find('.thumbnail').parent().attr('class'))
  859. .toContain('icon-loading-small');
  860. expect(moveStub.calledOnce).toEqual(true);
  861. return deferredMove.promise();
  862. });
  863. var promise = fileList.move('One.txt', '/somedir');
  864. deferredMove.reject(409);
  865. return promise.then(function(){
  866. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  867. expect(notificationStub.calledOnce).toEqual(true);
  868. expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
  869. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  870. .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
  871. }).then(done, done);
  872. });
  873. });
  874. describe('Copying files', function() {
  875. var deferredCopy;
  876. var copyStub;
  877. beforeEach(function() {
  878. deferredCopy = $.Deferred();
  879. copyStub = sinon.stub(filesClient, 'copy');
  880. fileList.setFiles(testFiles);
  881. });
  882. afterEach(function() {
  883. copyStub.restore();
  884. });
  885. it('Copies single file to target folder', function(done) {
  886. copyStub.callsFake(function(){
  887. expect(copyStub.calledOnce).toEqual(true);
  888. expect(copyStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  889. expect(copyStub.getCall(0).args[1]).toEqual('/somedir/One.txt');
  890. return deferredCopy.promise();
  891. });
  892. var promise = fileList.copy('One.txt', '/somedir');
  893. deferredCopy.resolve(201);
  894. return promise.then(function(){
  895. // File is still here
  896. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  897. // folder size has increased
  898. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  899. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  900. // Copying sents a notification to tell that we've successfully copied file
  901. expect(notificationStub.notCalled).toEqual(false);
  902. }).then(done, done);
  903. });
  904. it('Copies list of files to target folder', function(done) {
  905. var deferredCopy1 = $.Deferred();
  906. var deferredCopy2 = $.Deferred();
  907. copyStub.onCall(0).callsFake(function(src, dst){
  908. expect(src).toEqual('/subdir/One.txt');
  909. expect(dst).toEqual('/somedir/One.txt');
  910. return deferredCopy1.promise();
  911. });
  912. copyStub.onCall(1).callsFake(function(src, dst){
  913. // folder size has increased during copy
  914. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  915. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  916. expect(src).toEqual('/subdir/Two.jpg');
  917. expect(dst).toEqual('/somedir/Two.jpg');
  918. return deferredCopy2.promise();
  919. });
  920. var promise = fileList.copy(['One.txt', 'Two.jpg'], '/somedir');
  921. deferredCopy1.resolve(201);
  922. deferredCopy2.resolve(201);
  923. return promise.then(function(){
  924. expect(copyStub.calledTwice).toEqual(true);
  925. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  926. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  927. // folder size has increased
  928. expect(fileList.findFileEl('somedir').data('size')).toEqual(12311);
  929. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 KB');
  930. expect(notificationStub.notCalled).toEqual(false);
  931. }).then(done, done);
  932. });
  933. it('Shows notification if a file could not be copied', function(done) {
  934. copyStub.callsFake(function(){
  935. expect(copyStub.calledOnce).toEqual(true);
  936. return deferredCopy.promise();
  937. });
  938. var promise = fileList.copy('One.txt', '/somedir');
  939. deferredCopy.reject(409);
  940. return promise.then(function(){
  941. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  942. expect(notificationStub.calledOnce).toEqual(true);
  943. expect(notificationStub.getCall(0).args[0]).toEqual('Could not copy "One.txt"');
  944. }).then(done, done);
  945. });
  946. it('Restores thumbnail if a file could not be copied', function(done) {
  947. copyStub.callsFake(function(){
  948. expect(fileList.findFileEl('One.txt').find('.thumbnail').parent().attr('class'))
  949. .toContain('icon-loading-small');
  950. expect(copyStub.calledOnce).toEqual(true);
  951. return deferredCopy.promise();
  952. });
  953. var promise = fileList.copy('One.txt', '/somedir');
  954. deferredCopy.reject(409);
  955. return promise.then(function(){
  956. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  957. expect(notificationStub.calledOnce).toEqual(true);
  958. expect(notificationStub.getCall(0).args[0]).toEqual('Could not copy "One.txt"');
  959. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  960. .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
  961. }).then(done, done);
  962. });
  963. });
  964. describe('Update file', function() {
  965. it('does not change summary', function() {
  966. var $summary = $('.files-filestable .summary');
  967. var fileData = new FileInfo({
  968. type: 'file',
  969. name: 'test file',
  970. });
  971. var $tr = fileList.add(fileData);
  972. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  973. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  974. var model = fileList.getModelForFile('test file');
  975. model.set({size: '100'});
  976. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  977. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  978. });
  979. })
  980. describe('List rendering', function() {
  981. it('renders a list of files using add()', function() {
  982. expect(fileList.files.length).toEqual(0);
  983. expect(fileList.files).toEqual([]);
  984. fileList.setFiles(testFiles);
  985. expect($('.files-fileList tr').length).toEqual(4);
  986. expect(fileList.files.length).toEqual(4);
  987. expect(fileList.files).toEqual(testFiles);
  988. });
  989. it('updates summary using the file sizes', function() {
  990. var $summary;
  991. fileList.setFiles(testFiles);
  992. $summary = $('.files-filestable .summary');
  993. expect($summary.hasClass('hidden')).toEqual(false);
  994. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  995. expect($summary.find('.fileinfo').text()).toEqual('3 files');
  996. expect($summary.find('.filesize').text()).toEqual('69 KB');
  997. });
  998. it('shows headers, summary and hide empty content message after setting files', function(){
  999. fileList.setFiles(testFiles);
  1000. expect($('.files-filestable thead th').hasClass('hidden')).toEqual(false);
  1001. expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(true);
  1002. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(false);
  1003. });
  1004. it('hides headers, summary and show empty content message after setting empty file list', function(){
  1005. fileList.setFiles([]);
  1006. expect($('.files-filestable thead th').hasClass('hidden')).toEqual(true);
  1007. expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(false);
  1008. expect($('.emptyfilelist.emptycontent .uploadmessage').hasClass('hidden')).toEqual(false);
  1009. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
  1010. });
  1011. it('hides headers, upload message, and summary when list is empty and user has no creation permission', function(){
  1012. $('#permissions').val(0);
  1013. fileList.setFiles([]);
  1014. expect($('.files-filestable thead th').hasClass('hidden')).toEqual(true);
  1015. expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(false);
  1016. expect($('.emptyfilelist.emptycontent .uploadmessage').hasClass('hidden')).toEqual(true);
  1017. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
  1018. });
  1019. it('calling findFileEl() can find existing file element', function() {
  1020. fileList.setFiles(testFiles);
  1021. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  1022. });
  1023. it('calling findFileEl() returns empty when file not found in file', function() {
  1024. fileList.setFiles(testFiles);
  1025. expect(fileList.findFileEl('unexist.dat').length).toEqual(0);
  1026. });
  1027. it('only add file if in same current directory', function() {
  1028. fileList.changeDirectory('/current dir', false, true);
  1029. var fileData = {
  1030. type: 'file',
  1031. name: 'testFile.txt',
  1032. directory: '/current dir'
  1033. };
  1034. fileList.add(fileData);
  1035. expect(fileList.findFileEl('testFile.txt').length).toEqual(1);
  1036. });
  1037. it('triggers "fileActionsReady" event after update', function() {
  1038. var handler = sinon.stub();
  1039. fileList.$fileList.on('fileActionsReady', handler);
  1040. fileList.setFiles(testFiles);
  1041. expect(handler.calledOnce).toEqual(true);
  1042. expect(handler.getCall(0).args[0].$files.length).toEqual(testFiles.length);
  1043. });
  1044. it('triggers "fileActionsReady" event after single add', function() {
  1045. var handler = sinon.stub();
  1046. var $tr;
  1047. fileList.setFiles(testFiles);
  1048. fileList.$fileList.on('fileActionsReady', handler);
  1049. $tr = fileList.add({name: 'test.txt'});
  1050. expect(handler.calledOnce).toEqual(true);
  1051. expect(handler.getCall(0).args[0].$files.is($tr)).toEqual(true);
  1052. });
  1053. it('triggers "fileActionsReady" event after next page load with the newly appended files', function() {
  1054. var handler = sinon.stub();
  1055. fileList.setFiles(generateFiles(0, 64));
  1056. fileList.$fileList.on('fileActionsReady', handler);
  1057. fileList._nextPage();
  1058. expect(handler.calledOnce).toEqual(true);
  1059. expect(handler.getCall(0).args[0].$files.length).toEqual(fileList.pageSize());
  1060. });
  1061. it('does not trigger "fileActionsReady" event after single add with silent argument', function() {
  1062. var handler = sinon.stub();
  1063. fileList.setFiles(testFiles);
  1064. fileList.$fileList.on('fileActionsReady', handler);
  1065. fileList.add({name: 'test.txt'}, {silent: true});
  1066. expect(handler.notCalled).toEqual(true);
  1067. });
  1068. it('triggers "updated" event after update', function() {
  1069. var handler = sinon.stub();
  1070. fileList.$fileList.on('updated', handler);
  1071. fileList.setFiles(testFiles);
  1072. expect(handler.calledOnce).toEqual(true);
  1073. });
  1074. it('does not update summary when removing non-existing files', function() {
  1075. var $summary;
  1076. // single file
  1077. fileList.setFiles([testFiles[0]]);
  1078. $summary = $('.files-filestable .summary');
  1079. expect($summary.hasClass('hidden')).toEqual(false);
  1080. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1081. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1082. fileList.remove('unexist.txt');
  1083. expect($summary.hasClass('hidden')).toEqual(false);
  1084. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1085. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1086. });
  1087. });
  1088. describe('Filtered list rendering', function() {
  1089. it('filters the list of files using filter()', function() {
  1090. expect(fileList.files.length).toEqual(0);
  1091. expect(fileList.files).toEqual([]);
  1092. fileList.setFiles(testFiles);
  1093. var $summary = $('.files-filestable .summary');
  1094. var $nofilterresults = fileList.$el.find(".nofilterresults");
  1095. expect($nofilterresults.length).toEqual(1);
  1096. expect($summary.hasClass('hidden')).toEqual(false);
  1097. expect($('.files-fileList tr:not(.hidden)').length).toEqual(4);
  1098. expect(fileList.files.length).toEqual(4);
  1099. expect($summary.hasClass('hidden')).toEqual(false);
  1100. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1101. fileList.setFilter('e');
  1102. expect($('.files-fileList tr:not(.hidden)').length).toEqual(3);
  1103. expect(fileList.files.length).toEqual(4);
  1104. expect($summary.hasClass('hidden')).toEqual(false);
  1105. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  1106. expect($summary.find('.fileinfo').text()).toEqual('2 files');
  1107. expect($summary.find('.filter').text()).toEqual(" match \"e\"");
  1108. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1109. fileList.setFilter('ee');
  1110. expect($('.files-fileList tr:not(.hidden)').length).toEqual(1);
  1111. expect(fileList.files.length).toEqual(4);
  1112. expect($summary.hasClass('hidden')).toEqual(false);
  1113. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1114. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1115. expect($summary.find('.filter').text()).toEqual(" matches \"ee\"");
  1116. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1117. fileList.setFilter('eee');
  1118. expect($('.files-fileList tr:not(.hidden)').length).toEqual(0);
  1119. expect(fileList.files.length).toEqual(4);
  1120. expect($summary.hasClass('hidden')).toEqual(true);
  1121. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  1122. fileList.setFilter('ee');
  1123. expect($('.files-fileList tr:not(.hidden)').length).toEqual(1);
  1124. expect(fileList.files.length).toEqual(4);
  1125. expect($summary.hasClass('hidden')).toEqual(false);
  1126. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1127. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1128. expect($summary.find('.filter').text()).toEqual(" matches \"ee\"");
  1129. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1130. fileList.setFilter('e');
  1131. expect($('.files-fileList tr:not(.hidden)').length).toEqual(3);
  1132. expect(fileList.files.length).toEqual(4);
  1133. expect($summary.hasClass('hidden')).toEqual(false);
  1134. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  1135. expect($summary.find('.fileinfo').text()).toEqual('2 files');
  1136. expect($summary.find('.filter').text()).toEqual(" match \"e\"");
  1137. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1138. fileList.setFilter('');
  1139. expect($('.files-fileList tr:not(.hidden)').length).toEqual(4);
  1140. expect(fileList.files.length).toEqual(4);
  1141. expect($summary.hasClass('hidden')).toEqual(false);
  1142. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  1143. expect($summary.find('.fileinfo').text()).toEqual('3 files');
  1144. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1145. });
  1146. it('filters the list of non-rendered rows using filter()', function() {
  1147. var $summary = $('.files-filestable .summary');
  1148. var $nofilterresults = fileList.$el.find(".nofilterresults");
  1149. fileList.setFiles(generateFiles(0, 64));
  1150. fileList.setFilter('63');
  1151. expect($('.files-fileList tr:not(.hidden)').length).toEqual(1);
  1152. expect($summary.hasClass('hidden')).toEqual(false);
  1153. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1154. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1155. expect($summary.find('.filter').text()).toEqual(" matches \"63\"");
  1156. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1157. });
  1158. it('hides the emptyfiles notice when using filter()', function() {
  1159. expect(fileList.files.length).toEqual(0);
  1160. expect(fileList.files).toEqual([]);
  1161. fileList.setFiles([]);
  1162. var $summary = $('.files-filestable .summary');
  1163. var $emptycontent = fileList.$el.find(".emptyfilelist.emptycontent");
  1164. var $nofilterresults = fileList.$el.find(".nofilterresults");
  1165. expect($emptycontent.length).toEqual(1);
  1166. expect($nofilterresults.length).toEqual(1);
  1167. expect($('.files-fileList tr:not(.hidden)').length).toEqual(0);
  1168. expect(fileList.files.length).toEqual(0);
  1169. expect($summary.hasClass('hidden')).toEqual(true);
  1170. expect($emptycontent.hasClass('hidden')).toEqual(false);
  1171. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1172. fileList.setFilter('e');
  1173. expect($('.files-fileList tr:not(.hidden)').length).toEqual(0);
  1174. expect(fileList.files.length).toEqual(0);
  1175. expect($summary.hasClass('hidden')).toEqual(true);
  1176. expect($emptycontent.hasClass('hidden')).toEqual(true);
  1177. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  1178. fileList.setFilter('');
  1179. expect($('.files-fileList tr:not(.hidden)').length).toEqual(0);
  1180. expect(fileList.files.length).toEqual(0);
  1181. expect($summary.hasClass('hidden')).toEqual(true);
  1182. expect($emptycontent.hasClass('hidden')).toEqual(false);
  1183. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1184. });
  1185. it('does not show the emptyfiles or nofilterresults notice when the mask is active', function() {
  1186. expect(fileList.files.length).toEqual(0);
  1187. expect(fileList.files).toEqual([]);
  1188. fileList.showMask();
  1189. fileList.setFiles(testFiles);
  1190. var $emptycontent = fileList.$el.find(".emptyfilelist.emptycontent");
  1191. var $nofilterresults = fileList.$el.find(".nofilterresults");
  1192. expect($emptycontent.length).toEqual(1);
  1193. expect($nofilterresults.length).toEqual(1);
  1194. expect($emptycontent.hasClass('hidden')).toEqual(true);
  1195. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1196. /*
  1197. fileList.setFilter('e');
  1198. expect($emptycontent.hasClass('hidden')).toEqual(true);
  1199. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  1200. */
  1201. fileList.setFilter('');
  1202. expect($emptycontent.hasClass('hidden')).toEqual(true);
  1203. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1204. });
  1205. });
  1206. describe('Rendering next page on scroll', function() {
  1207. beforeEach(function() {
  1208. fileList.setFiles(generateFiles(0, 64));
  1209. });
  1210. it('renders only the first page', function() {
  1211. expect(fileList.files.length).toEqual(65);
  1212. expect($('.files-fileList tr').length).toEqual(20);
  1213. });
  1214. it('renders the full first page despite hidden rows', function() {
  1215. window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
  1216. var files = _.map(generateFiles(0, 23), function(data) {
  1217. return _.extend(data, {
  1218. name: '.' + data.name
  1219. });
  1220. });
  1221. // only hidden files + one visible
  1222. files.push(testFiles[0]);
  1223. fileList.setFiles(files);
  1224. expect(fileList.files.length).toEqual(25);
  1225. // render 24 hidden elements + the visible one
  1226. expect($('.files-fileList tr').length).toEqual(25);
  1227. });
  1228. it('renders the full first page despite hidden rows', function() {
  1229. window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true });
  1230. var files = _.map(generateFiles(0, 23), function(data) {
  1231. return _.extend(data, {
  1232. name: '.' + data.name
  1233. });
  1234. });
  1235. // only hidden files + one visible
  1236. files.push(testFiles[0]);
  1237. fileList.setFiles(files);
  1238. expect(fileList.files.length).toEqual(25);
  1239. // render 20 first hidden elements as visible
  1240. expect($('.files-fileList tr').length).toEqual(20);
  1241. });
  1242. it('renders the second page when scrolling down (trigger nextPage)', function() {
  1243. // TODO: can't simulate scrolling here, so calling nextPage directly
  1244. fileList._nextPage(true);
  1245. expect($('.files-fileList tr').length).toEqual(40);
  1246. fileList._nextPage(true);
  1247. expect($('.files-fileList tr').length).toEqual(60);
  1248. fileList._nextPage(true);
  1249. expect($('.files-fileList tr').length).toEqual(65);
  1250. fileList._nextPage(true);
  1251. // stays at 65
  1252. expect($('.files-fileList tr').length).toEqual(65);
  1253. });
  1254. it('inserts into the DOM if insertion point is in the visible page ', function() {
  1255. fileList.add({
  1256. id: 2000,
  1257. type: 'file',
  1258. name: 'File with index 15b.txt'
  1259. });
  1260. expect($('.files-fileList tr').length).toEqual(21);
  1261. expect(fileList.findFileEl('File with index 15b.txt').index()).toEqual(16);
  1262. });
  1263. it('does not inserts into the DOM if insertion point is not the visible page ', function() {
  1264. fileList.add({
  1265. id: 2000,
  1266. type: 'file',
  1267. name: 'File with index 28b.txt'
  1268. });
  1269. expect($('.files-fileList tr').length).toEqual(20);
  1270. expect(fileList.findFileEl('File with index 28b.txt').length).toEqual(0);
  1271. fileList._nextPage(true);
  1272. expect($('.files-fileList tr').length).toEqual(40);
  1273. expect(fileList.findFileEl('File with index 28b.txt').index()).toEqual(29);
  1274. });
  1275. it('appends into the DOM when inserting a file after the last visible element', function() {
  1276. fileList.add({
  1277. id: 2000,
  1278. type: 'file',
  1279. name: 'File with index 19b.txt'
  1280. });
  1281. expect($('.files-fileList tr').length).toEqual(21);
  1282. fileList._nextPage(true);
  1283. expect($('.files-fileList tr').length).toEqual(41);
  1284. });
  1285. it('appends into the DOM when inserting a file on the last page when visible', function() {
  1286. fileList._nextPage(true);
  1287. expect($('.files-fileList tr').length).toEqual(40);
  1288. fileList._nextPage(true);
  1289. expect($('.files-fileList tr').length).toEqual(60);
  1290. fileList._nextPage(true);
  1291. expect($('.files-fileList tr').length).toEqual(65);
  1292. fileList._nextPage(true);
  1293. fileList.add({
  1294. id: 2000,
  1295. type: 'file',
  1296. name: 'File with index 88.txt'
  1297. });
  1298. expect($('.files-fileList tr').length).toEqual(66);
  1299. fileList._nextPage(true);
  1300. expect($('.files-fileList tr').length).toEqual(66);
  1301. });
  1302. it('shows additional page when appending a page of files and scrolling down', function() {
  1303. var newFiles = generateFiles(66, 81);
  1304. for (var i = 0; i < newFiles.length; i++) {
  1305. fileList.add(newFiles[i]);
  1306. }
  1307. expect($('.files-fileList tr').length).toEqual(20);
  1308. fileList._nextPage(true);
  1309. expect($('.files-fileList tr').length).toEqual(40);
  1310. fileList._nextPage(true);
  1311. expect($('.files-fileList tr').length).toEqual(60);
  1312. fileList._nextPage(true);
  1313. expect($('.files-fileList tr').length).toEqual(80);
  1314. fileList._nextPage(true);
  1315. expect($('.files-fileList tr').length).toEqual(81);
  1316. fileList._nextPage(true);
  1317. expect($('.files-fileList tr').length).toEqual(81);
  1318. });
  1319. it('automatically renders next page when there are not enough elements visible', function() {
  1320. // delete the 15 first elements
  1321. for (var i = 0; i < 15; i++) {
  1322. fileList.remove(fileList.files[0].name);
  1323. }
  1324. // still makes sure that there are 20 elements visible, if any
  1325. expect($('.files-fileList tr').length).toEqual(25);
  1326. });
  1327. });
  1328. describe('file previews', function() {
  1329. var previewLoadStub;
  1330. beforeEach(function() {
  1331. previewLoadStub = sinon.stub(OCA.Files.FileList.prototype, 'lazyLoadPreview');
  1332. });
  1333. afterEach(function() {
  1334. previewLoadStub.restore();
  1335. });
  1336. it('renders default file icon when none provided and no mime type is set', function() {
  1337. var fileData = {
  1338. name: 'testFile.txt'
  1339. };
  1340. var $tr = fileList.add(fileData);
  1341. var $imgDiv = $tr.find('td.filename .thumbnail');
  1342. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/file.svg');
  1343. // tries to load preview
  1344. expect(previewLoadStub.calledOnce).toEqual(true);
  1345. });
  1346. it('renders default icon for folder when none provided', function() {
  1347. var fileData = {
  1348. name: 'test dir',
  1349. mimetype: 'httpd/unix-directory'
  1350. };
  1351. var $tr = fileList.add(fileData);
  1352. var $imgDiv = $tr.find('td.filename .thumbnail');
  1353. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/folder.svg');
  1354. // no preview since it's a directory
  1355. expect(previewLoadStub.notCalled).toEqual(true);
  1356. });
  1357. it('renders provided icon for file when provided', function() {
  1358. var fileData = new FileInfo({
  1359. type: 'file',
  1360. name: 'test file',
  1361. icon: OC.getRootPath() + '/core/img/filetypes/application-pdf.svg',
  1362. mimetype: 'application/pdf'
  1363. });
  1364. var $tr = fileList.add(fileData);
  1365. var $imgDiv = $tr.find('td.filename .thumbnail');
  1366. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/application-pdf.svg');
  1367. // try loading preview
  1368. expect(previewLoadStub.calledOnce).toEqual(true);
  1369. });
  1370. it('renders provided icon for file when provided', function() {
  1371. var fileData = new FileInfo({
  1372. name: 'somefile.pdf',
  1373. icon: OC.getRootPath() + '/core/img/filetypes/application-pdf.svg'
  1374. });
  1375. var $tr = fileList.add(fileData);
  1376. var $imgDiv = $tr.find('td.filename .thumbnail');
  1377. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/application-pdf.svg');
  1378. // try loading preview
  1379. expect(previewLoadStub.calledOnce).toEqual(true);
  1380. });
  1381. it('renders provided icon for folder when provided', function() {
  1382. var fileData = new FileInfo({
  1383. name: 'some folder',
  1384. mimetype: 'httpd/unix-directory',
  1385. icon: OC.getRootPath() + '/core/img/filetypes/folder-alt.svg'
  1386. });
  1387. var $tr = fileList.add(fileData);
  1388. var $imgDiv = $tr.find('td.filename .thumbnail');
  1389. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-alt.svg');
  1390. // do not load preview for folders
  1391. expect(previewLoadStub.notCalled).toEqual(true);
  1392. });
  1393. it('renders preview when no icon was provided', function() {
  1394. var fileData = {
  1395. type: 'file',
  1396. name: 'test file'
  1397. };
  1398. var $tr = fileList.add(fileData);
  1399. var $td = $tr.find('td.filename');
  1400. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail')))
  1401. .toEqual(OC.getRootPath() + '/core/img/filetypes/file.svg');
  1402. expect(previewLoadStub.calledOnce).toEqual(true);
  1403. // third argument is callback
  1404. previewLoadStub.getCall(0).args[0].callback(OC.getRootPath() + '/somepath.png');
  1405. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/somepath.png');
  1406. });
  1407. it('does not render preview for directories', function() {
  1408. var fileData = {
  1409. type: 'dir',
  1410. mimetype: 'httpd/unix-directory',
  1411. name: 'test dir'
  1412. };
  1413. var $tr = fileList.add(fileData);
  1414. var $td = $tr.find('td.filename');
  1415. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder.svg');
  1416. expect(previewLoadStub.notCalled).toEqual(true);
  1417. });
  1418. it('render encrypted folder icon for encrypted root', function() {
  1419. var fileData = {
  1420. type: 'dir',
  1421. mimetype: 'httpd/unix-directory',
  1422. name: 'test dir',
  1423. isEncrypted: true
  1424. };
  1425. var $tr = fileList.add(fileData);
  1426. var $td = $tr.find('td.filename');
  1427. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-encrypted.svg');
  1428. expect(previewLoadStub.notCalled).toEqual(true);
  1429. });
  1430. it('render encrypted folder icon for encrypted subdir', function() {
  1431. var fileData = {
  1432. type: 'dir',
  1433. mimetype: 'httpd/unix-directory',
  1434. name: 'test dir',
  1435. isEncrypted: true
  1436. };
  1437. var $tr = fileList.add(fileData);
  1438. var $td = $tr.find('td.filename');
  1439. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-encrypted.svg');
  1440. expect(previewLoadStub.notCalled).toEqual(true);
  1441. // default icon override
  1442. expect($tr.attr('data-icon')).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-encrypted.svg');
  1443. });
  1444. it('render external storage icon for external storage root', function() {
  1445. var fileData = {
  1446. type: 'dir',
  1447. mimetype: 'httpd/unix-directory',
  1448. name: 'test dir',
  1449. mountType: 'external-root'
  1450. };
  1451. var $tr = fileList.add(fileData);
  1452. var $td = $tr.find('td.filename');
  1453. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-external.svg');
  1454. expect(previewLoadStub.notCalled).toEqual(true);
  1455. });
  1456. it('render external storage icon for external storage subdir', function() {
  1457. var fileData = {
  1458. type: 'dir',
  1459. mimetype: 'httpd/unix-directory',
  1460. name: 'test dir',
  1461. mountType: 'external'
  1462. };
  1463. var $tr = fileList.add(fileData);
  1464. var $td = $tr.find('td.filename');
  1465. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-external.svg');
  1466. expect(previewLoadStub.notCalled).toEqual(true);
  1467. // default icon override
  1468. expect($tr.attr('data-icon')).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-external.svg');
  1469. });
  1470. });
  1471. describe('viewer mode', function() {
  1472. it('enabling viewer mode hides files table and action buttons', function() {
  1473. fileList.setViewerMode(true);
  1474. expect($('.files-filestable').hasClass('hidden')).toEqual(true);
  1475. expect($('.actions').hasClass('hidden')).toEqual(true);
  1476. expect($('.notCreatable').hasClass('hidden')).toEqual(true);
  1477. });
  1478. it('disabling viewer mode restores files table and action buttons', function() {
  1479. fileList.setViewerMode(true);
  1480. fileList.setViewerMode(false);
  1481. expect($('.files-filestable').hasClass('hidden')).toEqual(false);
  1482. expect($('.actions').hasClass('hidden')).toEqual(false);
  1483. expect($('.notCreatable').hasClass('hidden')).toEqual(true);
  1484. });
  1485. it('disabling viewer mode restores files table and action buttons with correct permissions', function() {
  1486. $('#permissions').val(0);
  1487. fileList.setViewerMode(true);
  1488. fileList.setViewerMode(false);
  1489. expect($('.files-filestable').hasClass('hidden')).toEqual(false);
  1490. expect($('.actions').hasClass('hidden')).toEqual(true);
  1491. expect($('.notCreatable').hasClass('hidden')).toEqual(false);
  1492. });
  1493. it('toggling viewer mode triggers event', function() {
  1494. var handler = sinon.stub();
  1495. fileList.$el.on('changeViewerMode', handler);
  1496. fileList.setViewerMode(true);
  1497. expect(handler.calledOnce).toEqual(true);
  1498. expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(true);
  1499. handler.reset();
  1500. fileList.setViewerMode(false);
  1501. expect(handler.calledOnce).toEqual(true);
  1502. expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(false);
  1503. });
  1504. });
  1505. describe('loading file list', function() {
  1506. var deferredList;
  1507. var getFolderContentsStub;
  1508. beforeEach(function() {
  1509. deferredList = $.Deferred();
  1510. getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
  1511. });
  1512. afterEach(function() {
  1513. getFolderContentsStub.restore();
  1514. });
  1515. it('fetches file list from server and renders it when reload() is called', function(done) {
  1516. var reloading = fileList.reload();
  1517. expect(getFolderContentsStub.calledOnce).toEqual(true);
  1518. expect(getFolderContentsStub.calledWith('/subdir')).toEqual(true);
  1519. deferredList.resolve(200, [testRoot].concat(testFiles));
  1520. return reloading.then(function() {
  1521. expect($('.files-fileList tr').length).toEqual(4);
  1522. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  1523. }).then(done, done);
  1524. });
  1525. it('switches dir and fetches file list when calling changeDirectory()', function() {
  1526. fileList.changeDirectory('/anothersubdir');
  1527. expect(fileList.getCurrentDirectory()).toEqual('/anothersubdir');
  1528. expect(getFolderContentsStub.calledOnce).toEqual(true);
  1529. expect(getFolderContentsStub.calledWith('/anothersubdir')).toEqual(true);
  1530. });
  1531. it('converts backslashes to slashes when calling changeDirectory()', function() {
  1532. fileList.changeDirectory('/another\\subdir');
  1533. expect(fileList.getCurrentDirectory()).toEqual('/another/subdir');
  1534. });
  1535. it('switches to root dir when current directory is invalid', function() {
  1536. _.each([
  1537. '..',
  1538. '/..',
  1539. '../',
  1540. '/../',
  1541. '/../abc',
  1542. '/abc/..',
  1543. '/abc/../',
  1544. '/../abc/',
  1545. '/foo%0Abar/',
  1546. '/foo%00bar/',
  1547. '/another\\subdir/../foo\\../bar\\..\\file/..\\folder/../'
  1548. ], function(path) {
  1549. fileList.changeDirectory(decodeURI(path));
  1550. expect(fileList.getCurrentDirectory()).toEqual('/');
  1551. });
  1552. });
  1553. it('allows paths with dotdot at the beginning or end', function() {
  1554. _.each([
  1555. '/..abc',
  1556. '/def..',
  1557. '/...',
  1558. '/abc../def'
  1559. ], function(path) {
  1560. fileList.changeDirectory(path);
  1561. expect(fileList.getCurrentDirectory()).toEqual(path);
  1562. });
  1563. });
  1564. it('switches to root dir when current directory does not exist', function(done) {
  1565. var changing = fileList.changeDirectory('/unexist');
  1566. deferredList.reject(404);
  1567. return changing.then(function() {
  1568. expect(fileList.getCurrentDirectory()).toEqual('/');
  1569. }).then(done, done);
  1570. });
  1571. it('switches to root dir when current directory returns 400', function(done) {
  1572. var changing = fileList.changeDirectory('/unexist');
  1573. deferredList.reject(400);
  1574. return changing.then(function() {
  1575. expect(fileList.getCurrentDirectory()).toEqual('/');
  1576. }).then(done, done);
  1577. });
  1578. it('switches to root dir when current directory returns 405', function(done) {
  1579. var changing = fileList.changeDirectory('/unexist');
  1580. deferredList.reject(405);
  1581. return changing.then(function() {
  1582. expect(fileList.getCurrentDirectory()).toEqual('/');
  1583. }).then(done, done);
  1584. });
  1585. it('switches to root dir when current directory is forbidden', function(done) {
  1586. var changing = fileList.changeDirectory('/unexist');
  1587. deferredList.reject(403);
  1588. return changing.then(function() {
  1589. expect(fileList.getCurrentDirectory()).toEqual('/');
  1590. }).then(done, done);
  1591. });
  1592. it('switches to root dir when current directory is unavailable', function(done) {
  1593. var changing = fileList.changeDirectory('/unexist');
  1594. deferredList.reject(500);
  1595. return changing.then(function() {
  1596. expect(fileList.getCurrentDirectory()).toEqual('/');
  1597. }).then(done, done);
  1598. });
  1599. it('shows mask before loading file list then hides it at the end', function(done) {
  1600. var showMaskStub = sinon.stub(fileList, 'showMask');
  1601. var hideMaskStub = sinon.stub(fileList, 'hideMask');
  1602. var changing = fileList.changeDirectory('/anothersubdir');
  1603. expect(showMaskStub.calledOnce).toEqual(true);
  1604. expect(hideMaskStub.calledOnce).toEqual(false);
  1605. deferredList.resolve(200, [testRoot].concat(testFiles));
  1606. return changing.then(function() {
  1607. expect(showMaskStub.calledOnce).toEqual(true);
  1608. expect(hideMaskStub.calledOnce).toEqual(true);
  1609. showMaskStub.restore();
  1610. hideMaskStub.restore();
  1611. }).then(done, done);
  1612. });
  1613. it('triggers "changeDirectory" event when changing directory', function(done) {
  1614. var handler = sinon.stub();
  1615. $('#app-content-files').on('changeDirectory', handler);
  1616. var changing = fileList.changeDirectory('/somedir');
  1617. deferredList.resolve(200, [testRoot].concat(testFiles));
  1618. return changing.then(function() {
  1619. expect(handler.calledOnce).toEqual(true);
  1620. expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
  1621. }).then(done, done);
  1622. });
  1623. it('triggers "afterChangeDirectory" event with fileid after changing directory', function(done) {
  1624. var handler = sinon.stub();
  1625. $('#app-content-files').on('afterChangeDirectory', handler);
  1626. var changing = fileList.changeDirectory('/somedir');
  1627. deferredList.resolve(200, [testRoot].concat(testFiles));
  1628. return changing.then(function() {
  1629. expect(handler.calledOnce).toEqual(true);
  1630. expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
  1631. expect(handler.getCall(0).args[0].fileId).toEqual(99);
  1632. }).then(done, done);
  1633. });
  1634. it('changes the directory when receiving "urlChanged" event', function() {
  1635. $('#app-content-files').trigger(new $.Event('urlChanged', {view: 'files', dir: '/somedir'}));
  1636. expect(fileList.getCurrentDirectory()).toEqual('/somedir');
  1637. });
  1638. it('refreshes breadcrumb after update', function() {
  1639. var setDirSpy = sinon.spy(fileList.breadcrumb, 'setDirectory');
  1640. fileList.changeDirectory('/anothersubdir');
  1641. deferredList.resolve(200, [testRoot].concat(testFiles));
  1642. // twice because setDirectory gets called by _setCurrentDir which
  1643. // gets called directly by changeDirectory and via reload()
  1644. expect(fileList.breadcrumb.setDirectory.calledTwice).toEqual(true);
  1645. expect(fileList.breadcrumb.setDirectory.calledWith('/anothersubdir')).toEqual(true);
  1646. setDirSpy.restore();
  1647. getFolderContentsStub.restore();
  1648. });
  1649. it('prepends a slash to directory if none was given', function() {
  1650. fileList.changeDirectory('');
  1651. expect(fileList.getCurrentDirectory()).toEqual('/');
  1652. fileList.changeDirectory('noslash');
  1653. expect(fileList.getCurrentDirectory()).toEqual('/noslash');
  1654. });
  1655. });
  1656. describe('breadcrumb events', function() {
  1657. var deferredList;
  1658. var getFolderContentsStub;
  1659. beforeEach(function() {
  1660. deferredList = $.Deferred();
  1661. getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
  1662. });
  1663. afterEach(function() {
  1664. getFolderContentsStub.restore();
  1665. });
  1666. it('clicking on root breadcrumb changes directory to root', function() {
  1667. fileList.changeDirectory('/subdir/two/three with space/four/five');
  1668. deferredList.resolve(200, [testRoot].concat(testFiles));
  1669. var changeDirStub = sinon.stub(fileList, 'changeDirectory');
  1670. fileList.breadcrumb.$el.find('.crumb:eq(1) > a').trigger({type: 'click', which: 1});
  1671. expect(changeDirStub.calledOnce).toEqual(true);
  1672. expect(changeDirStub.getCall(0).args[0]).toEqual('/');
  1673. changeDirStub.restore();
  1674. });
  1675. it('clicking on breadcrumb changes directory', function() {
  1676. fileList.changeDirectory('/subdir/two/three with space/four/five');
  1677. deferredList.resolve(200, [testRoot].concat(testFiles));
  1678. var changeDirStub = sinon.stub(fileList, 'changeDirectory');
  1679. fileList.breadcrumb.$el.find('.crumb:eq(4) > a').trigger({type: 'click', which: 1});
  1680. expect(changeDirStub.calledOnce).toEqual(true);
  1681. expect(changeDirStub.getCall(0).args[0]).toEqual('/subdir/two/three with space');
  1682. changeDirStub.restore();
  1683. });
  1684. it('dropping files on breadcrumb calls move operation', function() {
  1685. var testDir = '/subdir/two/three with space/four/five';
  1686. var moveStub = sinon.stub(filesClient, 'move');
  1687. var deferredMove1 = $.Deferred();
  1688. var deferredMove2 = $.Deferred();
  1689. moveStub.onCall(0).returns(deferredMove1.promise());
  1690. moveStub.onCall(1).returns(deferredMove2.promise());
  1691. fileList.changeDirectory(testDir);
  1692. deferredList.resolve(200, [testRoot].concat(testFiles));
  1693. var $crumb = fileList.breadcrumb.$el.find('.crumb:eq(4)');
  1694. // no idea what this is but is required by the handler
  1695. var ui = {
  1696. helper: {
  1697. find: sinon.stub()
  1698. }
  1699. };
  1700. // returns a list of tr that were dragged
  1701. ui.helper.find.returns([
  1702. $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
  1703. $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
  1704. ]);
  1705. // simulate drop event
  1706. var result = fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui).then(function(){
  1707. expect(moveStub.callCount).toEqual(2);
  1708. expect(moveStub.getCall(0).args[0]).toEqual(testDir + '/One.txt');
  1709. expect(moveStub.getCall(0).args[1]).toEqual('/subdir/two/three with space/One.txt');
  1710. expect(moveStub.getCall(1).args[0]).toEqual(testDir + '/Two.jpg');
  1711. expect(moveStub.getCall(1).args[1]).toEqual('/subdir/two/three with space/Two.jpg');
  1712. moveStub.restore();
  1713. });
  1714. deferredMove1.resolve(201);
  1715. deferredMove2.resolve(201);
  1716. return result;
  1717. });
  1718. it('dropping files on same dir breadcrumb does nothing', function() {
  1719. var testDir = '/subdir/two/three with space/four/five';
  1720. var moveStub = sinon.stub(filesClient, 'move').returns($.Deferred().promise());
  1721. fileList.changeDirectory(testDir);
  1722. deferredList.resolve(200, [testRoot].concat(testFiles));
  1723. var $crumb = fileList.breadcrumb.$el.find('.crumb:last');
  1724. // no idea what this is but is required by the handler
  1725. var ui = {
  1726. helper: {
  1727. find: sinon.stub()
  1728. }
  1729. };
  1730. // returns a list of tr that were dragged
  1731. ui.helper.find.returns([
  1732. $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
  1733. $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
  1734. ]);
  1735. // simulate drop event
  1736. fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui);
  1737. // no extra server request
  1738. expect(moveStub.notCalled).toEqual(true);
  1739. });
  1740. });
  1741. describe('Download Url', function() {
  1742. it('returns correct download URL for single files', function() {
  1743. expect(fileList.getDownloadUrl('some file.txt'))
  1744. .toEqual(OC.getRootPath() + '/remote.php/webdav/subdir/some%20file.txt');
  1745. expect(fileList.getDownloadUrl('some file.txt', '/anotherpath/abc'))
  1746. .toEqual(OC.getRootPath() + '/remote.php/webdav/anotherpath/abc/some%20file.txt');
  1747. fileList.changeDirectory('/', false, true);
  1748. expect(fileList.getDownloadUrl('some file.txt'))
  1749. .toEqual(OC.getRootPath() + '/remote.php/webdav/some%20file.txt');
  1750. });
  1751. it('returns correct download URL for multiple files', function() {
  1752. expect(fileList.getDownloadUrl(['a b c.txt', 'd e f.txt']))
  1753. .toEqual(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22a%20b%20c.txt%22%2C%22d%20e%20f.txt%22%5D');
  1754. });
  1755. it('returns the correct ajax URL', function() {
  1756. expect(fileList.getAjaxUrl('test', {a:1, b:'x y'}))
  1757. .toEqual(OC.getRootPath() + '/index.php/apps/files/ajax/test.php?a=1&b=x%20y');
  1758. });
  1759. });
  1760. describe('File selection', function() {
  1761. beforeEach(function() {
  1762. fileList.setFiles(testFiles);
  1763. });
  1764. it('Selects a file when clicking its checkbox', function() {
  1765. var $tr = fileList.findFileEl('One.txt');
  1766. expect($tr.find('input:checkbox').prop('checked')).toEqual(false);
  1767. $tr.find('td.selection input:checkbox').click();
  1768. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1769. });
  1770. it('Selects a range when clicking on one file then Shift clicking on another one', function() {
  1771. var $tr = fileList.findFileEl('One.txt');
  1772. var $tr2 = fileList.findFileEl('Three.pdf');
  1773. var e;
  1774. $tr.find('td.selection input:checkbox').click();
  1775. e = new $.Event('click');
  1776. e.shiftKey = true;
  1777. $tr2.find('td.filename .name').trigger(e);
  1778. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1779. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1780. expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
  1781. var selection = _.pluck(fileList.getSelectedFiles(), 'name');
  1782. expect(selection.length).toEqual(3);
  1783. expect(selection).toContain('One.txt');
  1784. expect(selection).toContain('Two.jpg');
  1785. expect(selection).toContain('Three.pdf');
  1786. });
  1787. it('Selects a range when clicking on one file then Shift clicking on another one that is above the first one', function() {
  1788. var $tr = fileList.findFileEl('One.txt');
  1789. var $tr2 = fileList.findFileEl('Three.pdf');
  1790. var e;
  1791. $tr2.find('td.selection input:checkbox').click();
  1792. e = new $.Event('click');
  1793. e.shiftKey = true;
  1794. $tr.find('td.filename .name').trigger(e);
  1795. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1796. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1797. expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
  1798. var selection = _.pluck(fileList.getSelectedFiles(), 'name');
  1799. expect(selection.length).toEqual(3);
  1800. expect(selection).toContain('One.txt');
  1801. expect(selection).toContain('Two.jpg');
  1802. expect(selection).toContain('Three.pdf');
  1803. });
  1804. it('Selecting all files will automatically check "select all" checkbox', function() {
  1805. expect($('.select-all').prop('checked')).toEqual(false);
  1806. $('.files-fileList tr td.selection input:checkbox').click();
  1807. expect($('.select-all').prop('checked')).toEqual(true);
  1808. });
  1809. it('Selecting all files on the first visible page will not automatically check "select all" checkbox', function() {
  1810. fileList.setFiles(generateFiles(0, 41));
  1811. expect($('.select-all').prop('checked')).toEqual(false);
  1812. $('.files-fileList tr td.selection input:checkbox').click();
  1813. expect($('.select-all').prop('checked')).toEqual(false);
  1814. });
  1815. it('Selecting all files also selects hidden files when invisible', function() {
  1816. window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
  1817. var $tr = fileList.add(new FileInfo({
  1818. name: '.hidden',
  1819. type: 'dir',
  1820. mimetype: 'httpd/unix-directory',
  1821. size: 150
  1822. }));
  1823. $('.select-all').click();
  1824. expect($tr.find('td.selection input:checkbox').prop('checked')).toEqual(true);
  1825. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toContain('.hidden');
  1826. });
  1827. it('Clicking "select all" will select/deselect all files', function() {
  1828. fileList.setFiles(generateFiles(0, 41));
  1829. $('.select-all').click();
  1830. expect($('.select-all').prop('checked')).toEqual(true);
  1831. $('.files-fileList tr input:checkbox').each(function() {
  1832. expect($(this).prop('checked')).toEqual(true);
  1833. });
  1834. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
  1835. $('.select-all').click();
  1836. expect($('.select-all').prop('checked')).toEqual(false);
  1837. $('.files-fileList tr input:checkbox').each(function() {
  1838. expect($(this).prop('checked')).toEqual(false);
  1839. });
  1840. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(0);
  1841. });
  1842. it('Clicking "select all" then deselecting a file will uncheck "select all"', function() {
  1843. $('.select-all').click();
  1844. expect($('.select-all').prop('checked')).toEqual(true);
  1845. var $tr = fileList.findFileEl('One.txt');
  1846. $tr.find('input:checkbox').click();
  1847. expect($('.select-all').prop('checked')).toEqual(false);
  1848. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1849. });
  1850. it('Updates the selection summary when doing a few manipulations with "Select all"', function() {
  1851. $('.select-all').click();
  1852. expect($('.select-all').prop('checked')).toEqual(true);
  1853. var $tr = fileList.findFileEl('One.txt');
  1854. // unselect one
  1855. $tr.find('input:checkbox').click();
  1856. expect($('.select-all').prop('checked')).toEqual(false);
  1857. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1858. // select all
  1859. $('.select-all').click();
  1860. expect($('.select-all').prop('checked')).toEqual(true);
  1861. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
  1862. // unselect one
  1863. $tr.find('input:checkbox').click();
  1864. expect($('.select-all').prop('checked')).toEqual(false);
  1865. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1866. // re-select it
  1867. $tr.find('input:checkbox').click();
  1868. expect($('.select-all').prop('checked')).toEqual(true);
  1869. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
  1870. });
  1871. it('Auto-selects files on next page when "select all" is checked', function() {
  1872. fileList.setFiles(generateFiles(0, 41));
  1873. $('.select-all').click();
  1874. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(20);
  1875. fileList._nextPage(true);
  1876. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(40);
  1877. fileList._nextPage(true);
  1878. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(42);
  1879. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
  1880. });
  1881. it('Selecting files updates selection summary', function() {
  1882. var $summary = $('.column-name a.name>span:first');
  1883. expect($summary.text()).toEqual('Name');
  1884. fileList.findFileEl('One.txt').find('input:checkbox').click();
  1885. fileList.findFileEl('Three.pdf').find('input:checkbox').click();
  1886. fileList.findFileEl('somedir').find('input:checkbox').click();
  1887. expect($summary.text()).toEqual('1 folder and 2 files');
  1888. });
  1889. it('Unselecting files hides selection summary', function() {
  1890. var $summary = $('.column-name a.name>span:first');
  1891. fileList.findFileEl('One.txt').find('input:checkbox').click().click();
  1892. expect($summary.text()).toEqual('Name');
  1893. });
  1894. it('Displays the number of hidden files in selection summary if hidden files are invisible', function() {
  1895. window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
  1896. var $tr = fileList.add(new FileInfo({
  1897. name: '.hidden',
  1898. type: 'dir',
  1899. mimetype: 'httpd/unix-directory',
  1900. size: 150
  1901. }));
  1902. $('.select-all').click();
  1903. var $summary = $('.column-name a.name>span:first');
  1904. expect($summary.text()).toEqual('2 folders and 3 files (including 1 hidden)');
  1905. });
  1906. it('Does not displays the number of hidden files in selection summary if hidden files are visible', function() {
  1907. window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true });
  1908. var $tr = fileList.add(new FileInfo({
  1909. name: '.hidden',
  1910. type: 'dir',
  1911. mimetype: 'httpd/unix-directory',
  1912. size: 150
  1913. }));
  1914. $('.select-all').click();
  1915. var $summary = $('.column-name a.name>span:first');
  1916. expect($summary.text()).toEqual('2 folders and 3 files');
  1917. });
  1918. it('Toggling hidden file visibility updates selection summary', function() {
  1919. window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
  1920. var $tr = fileList.add(new FileInfo({
  1921. name: '.hidden',
  1922. type: 'dir',
  1923. mimetype: 'httpd/unix-directory',
  1924. size: 150
  1925. }));
  1926. $('.select-all').click();
  1927. var $summary = $('.column-name a.name>span:first');
  1928. expect($summary.text()).toEqual('2 folders and 3 files (including 1 hidden)');
  1929. window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true });
  1930. expect($summary.text()).toEqual('2 folders and 3 files');
  1931. });
  1932. it('Select/deselect files shows/hides file actions', function() {
  1933. var $actions = $('.column-name .selectedActions');
  1934. var $checkbox = fileList.findFileEl('One.txt').find('input:checkbox');
  1935. expect($actions.hasClass('hidden')).toEqual(true);
  1936. $checkbox.click();
  1937. expect($actions.hasClass('hidden')).toEqual(false);
  1938. $checkbox.click();
  1939. expect($actions.hasClass('hidden')).toEqual(true);
  1940. });
  1941. it('Selection is cleared when switching dirs', function() {
  1942. $('.select-all').click();
  1943. var deferredList = $.Deferred();
  1944. var getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
  1945. fileList.changeDirectory('/');
  1946. deferredList.resolve(200, [testRoot].concat(testFiles));
  1947. expect($('.select-all').prop('checked')).toEqual(false);
  1948. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toEqual([]);
  1949. getFolderContentsStub.restore();
  1950. });
  1951. it('getSelectedFiles returns the selected files even when they are on the next page', function() {
  1952. var selectedFiles;
  1953. fileList.setFiles(generateFiles(0, 41));
  1954. $('.select-all').click();
  1955. // unselect one to not have the "allFiles" case
  1956. fileList.$fileList.find('tr input:checkbox:first').click();
  1957. // only 20 files visible, must still return all the selected ones
  1958. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1959. expect(selectedFiles.length).toEqual(41);
  1960. });
  1961. describe('clearing the selection', function() {
  1962. it('clears selected files selected individually calling setFiles()', function() {
  1963. var selectedFiles;
  1964. fileList.setFiles(generateFiles(0, 41));
  1965. fileList.$fileList.find('tr:eq(5) input:checkbox:first').click();
  1966. fileList.$fileList.find('tr:eq(7) input:checkbox:first').click();
  1967. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1968. expect(selectedFiles.length).toEqual(2);
  1969. fileList.setFiles(generateFiles(0, 2));
  1970. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1971. expect(selectedFiles.length).toEqual(0);
  1972. });
  1973. it('clears selected files selected with select all when calling setFiles()', function() {
  1974. var selectedFiles;
  1975. fileList.setFiles(generateFiles(0, 41));
  1976. $('.select-all').click();
  1977. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1978. expect(selectedFiles.length).toEqual(42);
  1979. fileList.setFiles(generateFiles(0, 2));
  1980. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1981. expect(selectedFiles.length).toEqual(0);
  1982. });
  1983. });
  1984. describe('Selection overlay', function() {
  1985. it('show doesnt show the copy/move action if one or more files are not copiable/movable', function () {
  1986. fileList.setFiles(testFiles);
  1987. $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_UPDATE);
  1988. $('.select-all').click();
  1989. expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(false);
  1990. expect(fileList.$el.find('.selectedActions .item-copyMove .label').text()).toEqual('Move or copy');
  1991. testFiles[0].permissions = OC.PERMISSION_READ;
  1992. $('.select-all').click();
  1993. fileList.setFiles(testFiles);
  1994. $('.select-all').click();
  1995. expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(false);
  1996. expect(fileList.$el.find('.selectedActions .item-copyMove .label').text()).toEqual('Copy');
  1997. testFiles[0].permissions = OC.PERMISSION_NONE;
  1998. $('.select-all').click();
  1999. fileList.setFiles(testFiles);
  2000. $('.select-all').click();
  2001. expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(true);
  2002. });
  2003. it('show doesnt show the download action if one or more files are not downloadable', function () {
  2004. fileList.setFiles(testFiles);
  2005. $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_UPDATE);
  2006. $('.select-all').click();
  2007. expect(fileList.$el.find('.selectedActions .item-download').hasClass('hidden')).toEqual(false);
  2008. testFiles[0].permissions = OC.PERMISSION_UPDATE;
  2009. $('.select-all').click();
  2010. fileList.setFiles(testFiles);
  2011. $('.select-all').click();
  2012. expect(fileList.$el.find('.selectedActions .item-download').hasClass('hidden')).toEqual(true);
  2013. });
  2014. it('show doesnt show the delete action if one or more files are not deletable', function () {
  2015. fileList.setFiles(testFiles);
  2016. $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_DELETE);
  2017. $('.select-all').click();
  2018. expect(fileList.$el.find('.selectedActions .item-delete').hasClass('hidden')).toEqual(false);
  2019. testFiles[0].permissions = OC.PERMISSION_READ;
  2020. $('.select-all').click();
  2021. fileList.setFiles(testFiles);
  2022. $('.select-all').click();
  2023. expect(fileList.$el.find('.selectedActions .item-delete').hasClass('hidden')).toEqual(true);
  2024. });
  2025. });
  2026. describe('Actions', function() {
  2027. beforeEach(function() {
  2028. fileList.findFileEl('One.txt').find('input:checkbox').click();
  2029. fileList.findFileEl('Three.pdf').find('input:checkbox').click();
  2030. fileList.findFileEl('somedir').find('input:checkbox').click();
  2031. });
  2032. it('getSelectedFiles returns the selected file data', function() {
  2033. var files = fileList.getSelectedFiles();
  2034. expect(files.length).toEqual(3);
  2035. expect(files[0]).toEqual({
  2036. id: 1,
  2037. name: 'One.txt',
  2038. mimetype: 'text/plain',
  2039. mtime: 123456789,
  2040. type: 'file',
  2041. size: 12,
  2042. etag: 'abc',
  2043. quotaAvailableBytes: '-1',
  2044. permissions: OC.PERMISSION_ALL,
  2045. hasPreview: true,
  2046. isEncrypted: false
  2047. });
  2048. expect(files[1]).toEqual({
  2049. id: 3,
  2050. type: 'file',
  2051. name: 'Three.pdf',
  2052. mimetype: 'application/pdf',
  2053. mtime: 234560000,
  2054. size: 58009,
  2055. quotaAvailableBytes: '-1',
  2056. etag: '123',
  2057. permissions: OC.PERMISSION_ALL,
  2058. hasPreview: true,
  2059. isEncrypted: false
  2060. });
  2061. expect(files[2]).toEqual({
  2062. id: 4,
  2063. type: 'dir',
  2064. name: 'somedir',
  2065. mimetype: 'httpd/unix-directory',
  2066. mtime: 134560000,
  2067. size: 250,
  2068. quotaAvailableBytes: '-1',
  2069. etag: '456',
  2070. permissions: OC.PERMISSION_ALL,
  2071. hasPreview: true,
  2072. isEncrypted: false
  2073. });
  2074. expect(files[0].id).toEqual(1);
  2075. expect(files[0].name).toEqual('One.txt');
  2076. expect(files[1].id).toEqual(3);
  2077. expect(files[1].name).toEqual('Three.pdf');
  2078. expect(files[2].id).toEqual(4);
  2079. expect(files[2].name).toEqual('somedir');
  2080. });
  2081. it('Removing a file removes it from the selection', function() {
  2082. fileList.remove('Three.pdf');
  2083. var files = fileList.getSelectedFiles();
  2084. expect(files.length).toEqual(2);
  2085. expect(files[0]).toEqual({
  2086. id: 1,
  2087. name: 'One.txt',
  2088. mimetype: 'text/plain',
  2089. mtime: 123456789,
  2090. type: 'file',
  2091. size: 12,
  2092. quotaAvailableBytes: '-1',
  2093. etag: 'abc',
  2094. permissions: OC.PERMISSION_ALL,
  2095. hasPreview: true,
  2096. isEncrypted: false
  2097. });
  2098. expect(files[1]).toEqual({
  2099. id: 4,
  2100. type: 'dir',
  2101. name: 'somedir',
  2102. mimetype: 'httpd/unix-directory',
  2103. mtime: 134560000,
  2104. size: 250,
  2105. quotaAvailableBytes: '-1',
  2106. etag: '456',
  2107. permissions: OC.PERMISSION_ALL,
  2108. hasPreview: true,
  2109. isEncrypted: false
  2110. });
  2111. });
  2112. describe('Download', function() {
  2113. beforeEach(function() {
  2114. fileList.$el.find('.actions-selected').click();
  2115. });
  2116. it('Opens download URL when clicking "Download"', function() {
  2117. $('.selectedActions .filesSelectMenu .download').click();
  2118. expect(redirectStub.calledOnce).toEqual(true);
  2119. expect(redirectStub.getCall(0).args[0]).toContain(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D');
  2120. redirectStub.restore();
  2121. });
  2122. it('Downloads root folder when all selected in root folder', function() {
  2123. fileList.changeDirectory('/', false, true);
  2124. $('.select-all').click();
  2125. $('.selectedActions .filesSelectMenu .download').click();
  2126. expect(redirectStub.calledOnce).toEqual(true);
  2127. expect(redirectStub.getCall(0).args[0]).toContain(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2F&files=');
  2128. });
  2129. it('Downloads parent folder when all selected in subfolder', function() {
  2130. $('.select-all').click();
  2131. $('.selectedActions .filesSelectMenu .download').click();
  2132. expect(redirectStub.calledOnce).toEqual(true);
  2133. expect(redirectStub.getCall(0).args[0]).toContain(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir');
  2134. });
  2135. afterEach(function() {
  2136. fileList.$el.find('.actions-selected').click();
  2137. });
  2138. });
  2139. describe('Delete', function() {
  2140. var deleteStub, deferredDelete;
  2141. beforeEach(function() {
  2142. deferredDelete = $.Deferred();
  2143. deleteStub = sinon.stub(filesClient, 'remove');
  2144. fileList.$el.find('.actions-selected').click();
  2145. });
  2146. afterEach(function() {
  2147. fileList.$el.find('.actions-selected').click();
  2148. deleteStub.restore();
  2149. });
  2150. it('Deletes selected files when "Delete" clicked', function(done) {
  2151. var deferred = $.Deferred();
  2152. deleteStub.returns(deferredDelete.promise());
  2153. deleteStub.onCall(2).callsFake(function(){
  2154. expect(deleteStub.callCount).toEqual(3);
  2155. expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  2156. expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Three.pdf');
  2157. expect(deleteStub.getCall(2).args[0]).toEqual('/subdir/somedir');
  2158. return deferredDelete.promise();
  2159. });
  2160. stub = sinon.stub(fileList._operationProgressBar, 'hideProgressBar').callsFake(function(){
  2161. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  2162. expect(fileList.findFileEl('Three.pdf').length).toEqual(0);
  2163. expect(fileList.findFileEl('somedir').length).toEqual(0);
  2164. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  2165. done();
  2166. deferred.resolve();
  2167. });
  2168. $('.selectedActions .filesSelectMenu .delete').click();
  2169. deferredDelete.resolve(204);
  2170. return deferred.promise();
  2171. });
  2172. it('Deletes all files when all selected when "Delete" clicked', function(done) {
  2173. var deferred = $.Deferred();
  2174. deleteStub.returns(deferredDelete.promise());
  2175. deleteStub.onCall(3).callsFake(function(){
  2176. expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  2177. expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Two.jpg');
  2178. expect(deleteStub.getCall(2).args[0]).toEqual('/subdir/Three.pdf');
  2179. expect(deleteStub.getCall(3).args[0]).toEqual('/subdir/somedir');
  2180. return deferredDelete.promise();
  2181. });
  2182. stub = sinon.stub(fileList._operationProgressBar, 'hideProgressBar').callsFake(function(){
  2183. expect(fileList.isEmpty).toEqual(true);
  2184. expect(deleteStub.callCount).toEqual(4);
  2185. done();
  2186. deferred.resolve();
  2187. });
  2188. $('.select-all').click();
  2189. $('.selectedActions .filesSelectMenu .delete').click();
  2190. deferredDelete.resolve(204);
  2191. return deferred.promise();
  2192. });
  2193. });
  2194. });
  2195. it('resets the file selection on reload', function() {
  2196. fileList.$el.find('.select-all').click();
  2197. fileList.reload();
  2198. expect(fileList.$el.find('.select-all').prop('checked')).toEqual(false);
  2199. expect(fileList.getSelectedFiles()).toEqual([]);
  2200. });
  2201. describe('Disabled selection', function() {
  2202. beforeEach(function() {
  2203. fileList._allowSelection = false;
  2204. fileList.setFiles(testFiles);
  2205. });
  2206. it('Does not render checkboxes', function() {
  2207. expect(fileList.$fileList.find('.selectCheckBox').length).toEqual(0);
  2208. });
  2209. it('Does not select a file with Ctrl or Shift if selection is not allowed', function() {
  2210. var $tr = fileList.findFileEl('One.txt');
  2211. var $tr2 = fileList.findFileEl('Three.pdf');
  2212. var e;
  2213. e = new $.Event('click');
  2214. e.ctrlKey = true;
  2215. $tr.find('td.filename .name').trigger(e);
  2216. // click on second entry, does not clear the selection
  2217. e = new $.Event('click');
  2218. e.ctrlKey = true;
  2219. $tr2.find('td.filename .name').trigger(e);
  2220. expect(fileList.getSelectedFiles().length).toEqual(0);
  2221. // deselect now
  2222. e = new $.Event('click');
  2223. e.shiftKey = true;
  2224. $tr2.find('td.filename .name').trigger(e);
  2225. expect(fileList.getSelectedFiles().length).toEqual(0);
  2226. });
  2227. });
  2228. });
  2229. describe('File actions', function() {
  2230. it('Clicking on a file name will trigger default action', function() {
  2231. var actionStub = sinon.stub();
  2232. fileList.setFiles(testFiles);
  2233. fileList.fileActions.registerAction({
  2234. mime: 'text/plain',
  2235. name: 'Test',
  2236. type: OCA.Files.FileActions.TYPE_INLINE,
  2237. permissions: OC.PERMISSION_ALL,
  2238. icon: function() {
  2239. // Specify icon for history button
  2240. return OC.imagePath('core','actions/history');
  2241. },
  2242. actionHandler: actionStub
  2243. });
  2244. fileList.fileActions.setDefault('text/plain', 'Test');
  2245. var $tr = fileList.findFileEl('One.txt');
  2246. $tr.find('td.filename .nametext').click();
  2247. expect(actionStub.calledOnce).toEqual(true);
  2248. expect(actionStub.getCall(0).args[0]).toEqual('One.txt');
  2249. var context = actionStub.getCall(0).args[1];
  2250. expect(context.$file.is($tr)).toEqual(true);
  2251. expect(context.fileList).toBeDefined();
  2252. expect(context.fileActions).toBeDefined();
  2253. expect(context.dir).toEqual('/subdir');
  2254. });
  2255. it('Clicking on an empty space of the file row will trigger the "Details" action', function() {
  2256. var detailsActionStub = sinon.stub();
  2257. fileList.setFiles(testFiles);
  2258. // Override the "Details" action set internally by the FileList for
  2259. // easier testing.
  2260. fileList.fileActions.registerAction({
  2261. mime: 'all',
  2262. name: 'Details',
  2263. permissions: OC.PERMISSION_NONE,
  2264. actionHandler: detailsActionStub
  2265. });
  2266. // Ensure that the action works even if fileActions.currentFile is
  2267. // not set.
  2268. fileList.fileActions.currentFile = null;
  2269. var $tr = fileList.findFileEl('One.txt');
  2270. $tr.find('td.filesize').click();
  2271. expect(detailsActionStub.calledOnce).toEqual(true);
  2272. expect(detailsActionStub.getCall(0).args[0]).toEqual('One.txt');
  2273. var context = detailsActionStub.getCall(0).args[1];
  2274. expect(context.$file.is($tr)).toEqual(true);
  2275. expect(context.fileList).toBe(fileList);
  2276. expect(context.fileActions).toBe(fileList.fileActions);
  2277. expect(context.dir).toEqual('/subdir');
  2278. });
  2279. it('redisplays actions when new actions have been registered', function() {
  2280. var actionStub = sinon.stub();
  2281. var readyHandler = sinon.stub();
  2282. var clock = sinon.useFakeTimers();
  2283. var debounceStub = sinon.stub(_, 'debounce').callsFake(function(callback) {
  2284. return function() {
  2285. // defer instead of debounce, to make it work with clock
  2286. _.defer(callback);
  2287. };
  2288. });
  2289. // need to reinit the list to make the debounce call
  2290. fileList.destroy();
  2291. fileList = new OCA.Files.FileList($('#app-content-files'));
  2292. fileList.setFiles(testFiles);
  2293. fileList.$fileList.on('fileActionsReady', readyHandler);
  2294. fileList.fileActions.registerAction({
  2295. mime: 'text/plain',
  2296. name: 'Test',
  2297. type: OCA.Files.FileActions.TYPE_INLINE,
  2298. permissions: OC.PERMISSION_ALL,
  2299. icon: function() {
  2300. // Specify icon for history button
  2301. return OC.imagePath('core','actions/history');
  2302. },
  2303. actionHandler: actionStub
  2304. });
  2305. var $tr = fileList.findFileEl('One.txt');
  2306. expect($tr.find('.action-test').length).toEqual(0);
  2307. expect(readyHandler.notCalled).toEqual(true);
  2308. // update is delayed
  2309. clock.tick(100);
  2310. expect($tr.find('.action-test').length).toEqual(1);
  2311. expect(readyHandler.calledOnce).toEqual(true);
  2312. clock.restore();
  2313. debounceStub.restore();
  2314. });
  2315. });
  2316. describe('Sorting files', function() {
  2317. var getCurrentUserStub;
  2318. beforeEach(function() {
  2319. getCurrentUserStub = sinon.stub(OC, 'getCurrentUser').returns({
  2320. uid: 1,
  2321. displayName: 'user1'
  2322. });
  2323. });
  2324. afterEach(function() {
  2325. getCurrentUserStub.restore();
  2326. });
  2327. it('Toggles the sort indicator when clicking on a column header', function() {
  2328. var ASC_CLASS = fileList.SORT_INDICATOR_ASC_CLASS;
  2329. var DESC_CLASS = fileList.SORT_INDICATOR_DESC_CLASS;
  2330. var request;
  2331. var sortingUrl = OC.generateUrl('/apps/files/api/v1/sorting');
  2332. fileList.$el.find('.column-size .columntitle').click();
  2333. // moves triangle to size column, check indicator on name is hidden
  2334. expect(
  2335. fileList.$el.find('.column-name .sort-indicator').hasClass('hidden')
  2336. ).toEqual(true);
  2337. // check indicator on size is visible and defaults to descending
  2338. expect(
  2339. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  2340. ).toEqual(false);
  2341. expect(
  2342. fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
  2343. ).toEqual(true);
  2344. // check if changes are persisted
  2345. expect(fakeServer.requests.length).toEqual(1);
  2346. request = fakeServer.requests[0];
  2347. expect(request.url).toEqual(sortingUrl);
  2348. // click again on size column, reverses direction
  2349. fileList.$el.find('.column-size .columntitle').click();
  2350. expect(
  2351. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  2352. ).toEqual(false);
  2353. expect(
  2354. fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS)
  2355. ).toEqual(true);
  2356. // check if changes are persisted
  2357. expect(fakeServer.requests.length).toEqual(2);
  2358. request = fakeServer.requests[1];
  2359. expect(request.url).toEqual(sortingUrl);
  2360. // click again on size column, reverses direction
  2361. fileList.$el.find('.column-size .columntitle').click();
  2362. expect(
  2363. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  2364. ).toEqual(false);
  2365. expect(
  2366. fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
  2367. ).toEqual(true);
  2368. expect(fakeServer.requests.length).toEqual(3);
  2369. request = fakeServer.requests[2];
  2370. expect(request.url).toEqual(sortingUrl);
  2371. // click on mtime column, moves indicator there
  2372. fileList.$el.find('.column-mtime .columntitle').click();
  2373. expect(
  2374. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  2375. ).toEqual(true);
  2376. expect(
  2377. fileList.$el.find('.column-mtime .sort-indicator').hasClass('hidden')
  2378. ).toEqual(false);
  2379. expect(
  2380. fileList.$el.find('.column-mtime .sort-indicator').hasClass(DESC_CLASS)
  2381. ).toEqual(true);
  2382. expect(fakeServer.requests.length).toEqual(4);
  2383. request = fakeServer.requests[3];
  2384. expect(request.url).toEqual(sortingUrl);
  2385. });
  2386. it('Uses correct sort comparator when inserting files', function() {
  2387. testFiles.sort(OCA.Files.FileList.Comparators.size);
  2388. testFiles.reverse(); //default is descending
  2389. fileList.setFiles(testFiles);
  2390. fileList.$el.find('.column-size .columntitle').click();
  2391. var newFileData = new FileInfo({
  2392. id: 999,
  2393. name: 'new file.txt',
  2394. mimetype: 'text/plain',
  2395. size: 40001,
  2396. etag: '999'
  2397. });
  2398. fileList.add(newFileData);
  2399. expect(fileList.findFileEl('Three.pdf').index()).toEqual(0);
  2400. expect(fileList.findFileEl('new file.txt').index()).toEqual(1);
  2401. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  2402. expect(fileList.findFileEl('somedir').index()).toEqual(3);
  2403. expect(fileList.findFileEl('One.txt').index()).toEqual(4);
  2404. expect(fileList.files.length).toEqual(5);
  2405. expect(fileList.$fileList.find('tr').length).toEqual(5);
  2406. });
  2407. it('Uses correct reversed sort comparator when inserting files', function() {
  2408. testFiles.sort(OCA.Files.FileList.Comparators.size);
  2409. fileList.setFiles(testFiles);
  2410. fileList.$el.find('.column-size .columntitle').click();
  2411. // reverse sort
  2412. fileList.$el.find('.column-size .columntitle').click();
  2413. var newFileData = new FileInfo({
  2414. id: 999,
  2415. name: 'new file.txt',
  2416. mimetype: 'text/plain',
  2417. size: 40001,
  2418. etag: '999'
  2419. });
  2420. fileList.add(newFileData);
  2421. expect(fileList.findFileEl('One.txt').index()).toEqual(0);
  2422. expect(fileList.findFileEl('somedir').index()).toEqual(1);
  2423. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  2424. expect(fileList.findFileEl('new file.txt').index()).toEqual(3);
  2425. expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
  2426. expect(fileList.files.length).toEqual(5);
  2427. expect(fileList.$fileList.find('tr').length).toEqual(5);
  2428. });
  2429. it('does not sort when clicking on header whenever multiselect is enabled', function() {
  2430. var sortStub = sinon.stub(OCA.Files.FileList.prototype, 'setSort');
  2431. fileList.setFiles(testFiles);
  2432. fileList.findFileEl('One.txt').find('input:checkbox:first').click();
  2433. fileList.$el.find('.column-size .columntitle').click();
  2434. expect(sortStub.notCalled).toEqual(true);
  2435. // can sort again after deselecting
  2436. fileList.findFileEl('One.txt').find('input:checkbox:first').click();
  2437. fileList.$el.find('.column-size .columntitle').click();
  2438. expect(sortStub.calledOnce).toEqual(true);
  2439. sortStub.restore();
  2440. });
  2441. describe('if no user logged in', function() {
  2442. beforeEach(function() {
  2443. getCurrentUserStub.returns({
  2444. uid: null,
  2445. displayName: 'Guest'
  2446. });
  2447. });
  2448. it('shouldn\'t send an update sort order request', function() {
  2449. OC.currentUser = false;
  2450. fileList.$el.find('.column-size .columntitle').click();
  2451. // check if there was no request
  2452. expect(fakeServer.requests.length).toEqual(0);
  2453. });
  2454. });
  2455. describe('with favorites', function() {
  2456. it('shows favorite files on top', function() {
  2457. testFiles.push(new FileInfo({
  2458. id: 5,
  2459. type: 'file',
  2460. name: 'ZZY Before last file in ascending order',
  2461. mimetype: 'text/plain',
  2462. mtime: 999999998,
  2463. size: 9999998,
  2464. // Tags would be added by TagsPlugin
  2465. tags: [OC.TAG_FAVORITE],
  2466. }), new FileInfo({
  2467. id: 6,
  2468. type: 'file',
  2469. name: 'ZZZ Last file in ascending order',
  2470. mimetype: 'text/plain',
  2471. mtime: 999999999,
  2472. size: 9999999,
  2473. // Tags would be added by TagsPlugin
  2474. tags: [OC.TAG_FAVORITE],
  2475. }));
  2476. fileList.setFiles(testFiles);
  2477. // Sort by name in ascending order (default sorting is by name
  2478. // in ascending order, but setFiles does not trigger a sort, so
  2479. // the files must be sorted before being set or a sort must be
  2480. // triggered afterwards by clicking on the header).
  2481. fileList.$el.find('.column-name .columntitle').click();
  2482. fileList.$el.find('.column-name .columntitle').click();
  2483. expect(fileList.findFileEl('ZZY Before last file in ascending order').index()).toEqual(0);
  2484. expect(fileList.findFileEl('ZZZ Last file in ascending order').index()).toEqual(1);
  2485. expect(fileList.findFileEl('somedir').index()).toEqual(2);
  2486. expect(fileList.findFileEl('One.txt').index()).toEqual(3);
  2487. expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
  2488. expect(fileList.findFileEl('Two.jpg').index()).toEqual(5);
  2489. // Sort by size in ascending order
  2490. fileList.$el.find('.column-size .columntitle').click();
  2491. fileList.$el.find('.column-size .columntitle').click();
  2492. expect(fileList.findFileEl('ZZY Before last file in ascending order').index()).toEqual(0);
  2493. expect(fileList.findFileEl('ZZZ Last file in ascending order').index()).toEqual(1);
  2494. expect(fileList.findFileEl('One.txt').index()).toEqual(2);
  2495. expect(fileList.findFileEl('somedir').index()).toEqual(3);
  2496. expect(fileList.findFileEl('Two.jpg').index()).toEqual(4);
  2497. expect(fileList.findFileEl('Three.pdf').index()).toEqual(5);
  2498. // Sort by modification time in ascending order
  2499. fileList.$el.find('.column-mtime .columntitle').click();
  2500. fileList.$el.find('.column-mtime .columntitle').click();
  2501. expect(fileList.findFileEl('ZZY Before last file in ascending order').index()).toEqual(0);
  2502. expect(fileList.findFileEl('ZZZ Last file in ascending order').index()).toEqual(1);
  2503. expect(fileList.findFileEl('One.txt').index()).toEqual(2);
  2504. expect(fileList.findFileEl('somedir').index()).toEqual(3);
  2505. expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
  2506. expect(fileList.findFileEl('Two.jpg').index()).toEqual(5);
  2507. });
  2508. it('shows favorite files on top also when using descending order', function() {
  2509. testFiles.push(new FileInfo({
  2510. id: 5,
  2511. type: 'file',
  2512. name: 'AAB Before last file in descending order',
  2513. mimetype: 'text/plain',
  2514. mtime: 2,
  2515. size: 2,
  2516. // Tags would be added by TagsPlugin
  2517. tags: [OC.TAG_FAVORITE],
  2518. }), new FileInfo({
  2519. id: 6,
  2520. type: 'file',
  2521. name: 'AAA Last file in descending order',
  2522. mimetype: 'text/plain',
  2523. mtime: 1,
  2524. size: 1,
  2525. // Tags would be added by TagsPlugin
  2526. tags: [OC.TAG_FAVORITE],
  2527. }));
  2528. fileList.setFiles(testFiles);
  2529. // Sort by name in descending order
  2530. fileList.$el.find('.column-name .columntitle').click();
  2531. expect(fileList.findFileEl('AAB Before last file in descending order').index()).toEqual(0);
  2532. expect(fileList.findFileEl('AAA Last file in descending order').index()).toEqual(1);
  2533. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  2534. expect(fileList.findFileEl('Three.pdf').index()).toEqual(3);
  2535. expect(fileList.findFileEl('One.txt').index()).toEqual(4);
  2536. expect(fileList.findFileEl('somedir').index()).toEqual(5);
  2537. // Sort by size in descending order
  2538. fileList.$el.find('.column-size .columntitle').click();
  2539. expect(fileList.findFileEl('AAB Before last file in descending order').index()).toEqual(0);
  2540. expect(fileList.findFileEl('AAA Last file in descending order').index()).toEqual(1);
  2541. expect(fileList.findFileEl('Three.pdf').index()).toEqual(2);
  2542. expect(fileList.findFileEl('Two.jpg').index()).toEqual(3);
  2543. expect(fileList.findFileEl('somedir').index()).toEqual(4);
  2544. expect(fileList.findFileEl('One.txt').index()).toEqual(5);
  2545. // Sort by modification time in descending order
  2546. fileList.$el.find('.column-mtime .columntitle').click();
  2547. expect(fileList.findFileEl('AAB Before last file in descending order').index()).toEqual(0);
  2548. expect(fileList.findFileEl('AAA Last file in descending order').index()).toEqual(1);
  2549. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  2550. expect(fileList.findFileEl('Three.pdf').index()).toEqual(3);
  2551. expect(fileList.findFileEl('somedir').index()).toEqual(4);
  2552. expect(fileList.findFileEl('One.txt').index()).toEqual(5);
  2553. });
  2554. });
  2555. });
  2556. describe('create file', function() {
  2557. var deferredCreate;
  2558. var deferredInfo;
  2559. var createStub;
  2560. var getFileInfoStub;
  2561. beforeEach(function() {
  2562. deferredCreate = $.Deferred();
  2563. deferredInfo = $.Deferred();
  2564. createStub = sinon.stub(filesClient, 'putFileContents')
  2565. .returns(deferredCreate.promise());
  2566. getFileInfoStub = sinon.stub(filesClient, 'getFileInfo')
  2567. .returns(deferredInfo.promise());
  2568. });
  2569. afterEach(function() {
  2570. createStub.restore();
  2571. getFileInfoStub.restore();
  2572. });
  2573. it('creates file with given name and adds it to the list', function(done) {
  2574. var creating = fileList.createFile('test.txt');
  2575. expect(createStub.calledOnce).toEqual(true);
  2576. expect(createStub.getCall(0).args[0]).toEqual('/subdir/test.txt');
  2577. expect(createStub.getCall(0).args[2]).toEqual({
  2578. contentType: 'text/plain',
  2579. overwrite: true
  2580. });
  2581. deferredCreate.resolve(200);
  2582. expect(getFileInfoStub.calledOnce).toEqual(true);
  2583. expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/test.txt');
  2584. deferredInfo.resolve(
  2585. 200,
  2586. new FileInfo({
  2587. path: '/subdir',
  2588. name: 'test.txt',
  2589. mimetype: 'text/plain'
  2590. })
  2591. );
  2592. return creating.then(function() {
  2593. var $tr = fileList.findFileEl('test.txt');
  2594. expect($tr.length).toEqual(1);
  2595. expect($tr.attr('data-mime')).toEqual('text/plain');
  2596. }).then(done, done);
  2597. });
  2598. // TODO: error cases
  2599. // TODO: unique name cases
  2600. });
  2601. describe('create folder', function() {
  2602. var deferredCreate;
  2603. var deferredInfo;
  2604. var createStub;
  2605. var getFileInfoStub;
  2606. beforeEach(function() {
  2607. deferredCreate = $.Deferred();
  2608. deferredInfo = $.Deferred();
  2609. createStub = sinon.stub(filesClient, 'createDirectory')
  2610. .returns(deferredCreate.promise());
  2611. getFileInfoStub = sinon.stub(filesClient, 'getFileInfo')
  2612. .returns(deferredInfo.promise());
  2613. });
  2614. afterEach(function() {
  2615. createStub.restore();
  2616. getFileInfoStub.restore();
  2617. });
  2618. it('creates folder with given name and adds it to the list', function(done) {
  2619. var creating = fileList.createDirectory('sub dir');
  2620. expect(createStub.calledOnce).toEqual(true);
  2621. expect(createStub.getCall(0).args[0]).toEqual('/subdir/sub dir');
  2622. deferredCreate.resolve(200);
  2623. expect(getFileInfoStub.calledOnce).toEqual(true);
  2624. expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/sub dir');
  2625. deferredInfo.resolve(
  2626. 200,
  2627. new FileInfo({
  2628. path: '/subdir',
  2629. name: 'sub dir',
  2630. mimetype: 'httpd/unix-directory'
  2631. })
  2632. );
  2633. return creating.then(function() {
  2634. var $tr = fileList.findFileEl('sub dir');
  2635. expect($tr.length).toEqual(1);
  2636. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  2637. }).then(done, done);
  2638. });
  2639. // TODO: error cases
  2640. // TODO: unique name cases
  2641. });
  2642. describe('addAndFetchFileInfo', function() {
  2643. var getFileInfoStub;
  2644. var getFileInfoDeferred;
  2645. beforeEach(function() {
  2646. getFileInfoDeferred = $.Deferred();
  2647. getFileInfoStub = sinon.stub(OC.Files.Client.prototype, 'getFileInfo');
  2648. getFileInfoStub.returns(getFileInfoDeferred.promise());
  2649. });
  2650. afterEach(function() {
  2651. getFileInfoStub.restore();
  2652. });
  2653. it('does not fetch if the given folder is not the current one', function() {
  2654. var promise = fileList.addAndFetchFileInfo('testfile.txt', '/another');
  2655. expect(getFileInfoStub.notCalled).toEqual(true);
  2656. expect(promise.state()).toEqual('resolved');
  2657. });
  2658. it('fetches info when folder is the current one', function(done) {
  2659. fileList.addAndFetchFileInfo('testfile.txt', '/subdir');
  2660. return Promise.resolve().then(function() {
  2661. expect(getFileInfoStub.calledOnce).toEqual(true);
  2662. expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/testfile.txt');
  2663. }).then(done, done);
  2664. });
  2665. it('adds file data to list when fetching is done', function(done) {
  2666. var adding = fileList.addAndFetchFileInfo('testfile.txt', '/subdir');
  2667. getFileInfoDeferred.resolve(200, {
  2668. name: 'testfile.txt',
  2669. size: 100
  2670. });
  2671. return adding.then(function() {
  2672. expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
  2673. }).then(done, done);
  2674. });
  2675. it('replaces file data to list when fetching is done', function(done) {
  2676. var adding = fileList.addAndFetchFileInfo('testfile.txt', '/subdir', {replace: true});
  2677. fileList.add({
  2678. name: 'testfile.txt',
  2679. size: 95
  2680. });
  2681. getFileInfoDeferred.resolve(200, {
  2682. name: 'testfile.txt',
  2683. size: 100
  2684. });
  2685. expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('95');
  2686. return adding.then(function() {
  2687. expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
  2688. }).then(done, done);
  2689. });
  2690. it('resolves promise with file data when fetching is done', function(done) {
  2691. var promise = fileList.addAndFetchFileInfo('testfile.txt', '/subdir', {replace: true});
  2692. getFileInfoDeferred.resolve(200, {
  2693. name: 'testfile.txt',
  2694. size: 100
  2695. });
  2696. expect(promise.state()).toEqual('pending');
  2697. return promise.then(function(status, data) {
  2698. expect(promise.state()).toEqual('resolved');
  2699. expect(status).toEqual(200);
  2700. expect(data.name).toEqual('testfile.txt');
  2701. expect(data.size).toEqual(100);
  2702. }).then(done, done);
  2703. });
  2704. });
  2705. /**
  2706. * Test upload mostly by testing the code inside the event handlers
  2707. * that were registered on the magic upload object
  2708. */
  2709. describe('file upload', function() {
  2710. var uploadData;
  2711. var uploader;
  2712. beforeEach(function() {
  2713. fileList.setFiles(testFiles);
  2714. uploader = fileList._uploader;
  2715. // simulate data structure from jquery.upload
  2716. uploadData = {
  2717. files: [{
  2718. name: 'upload.txt'
  2719. }]
  2720. };
  2721. });
  2722. afterEach(function() {
  2723. uploader = null;
  2724. uploadData = null;
  2725. });
  2726. describe('enableupload', function() {
  2727. it('sets up uploader when enableUpload is true', function() {
  2728. expect(fileList._uploader).toBeDefined();
  2729. });
  2730. it('does not sets up uploader when enableUpload is false', function() {
  2731. fileList.destroy();
  2732. fileList = new OCA.Files.FileList($('#app-content-files'), {
  2733. filesClient: filesClient
  2734. });
  2735. expect(fileList._uploader).toBeFalsy();
  2736. });
  2737. });
  2738. describe('adding files for upload', function() {
  2739. /**
  2740. * Simulate add event on the given target
  2741. *
  2742. * @return event object including the result
  2743. */
  2744. function addFile(data) {
  2745. uploader.trigger('add', {}, data || {});
  2746. }
  2747. it('sets target dir to the current directory', function() {
  2748. addFile(uploadData);
  2749. expect(uploadData.targetDir).toEqual('/subdir');
  2750. });
  2751. });
  2752. describe('dropping external files', function() {
  2753. /**
  2754. * Simulate drop event on the given target
  2755. *
  2756. * @param $target target element to drop on
  2757. * @return event object including the result
  2758. */
  2759. function dropOn($target, data) {
  2760. var eventData = {
  2761. delegatedEvent: {
  2762. target: $target
  2763. },
  2764. preventDefault: function () {
  2765. },
  2766. stopPropagation: function() {
  2767. }
  2768. };
  2769. uploader.trigger('drop', eventData, data || {});
  2770. return !!data.targetDir;
  2771. }
  2772. it('drop on a tr or crumb outside file list does not trigger upload', function() {
  2773. var $anotherTable = $('<table><tbody><tr><td>outside<div class="crumb">crumb</div></td></tr></table>');
  2774. var ev;
  2775. $('#testArea').append($anotherTable);
  2776. ev = dropOn($anotherTable.find('tr'), uploadData);
  2777. expect(ev).toEqual(false);
  2778. ev = dropOn($anotherTable.find('.crumb'), uploadData);
  2779. expect(ev).toEqual(false);
  2780. });
  2781. it('drop on an element outside file list container does not trigger upload', function() {
  2782. var $anotherEl = $('<div>outside</div>');
  2783. var ev;
  2784. $('#testArea').append($anotherEl);
  2785. ev = dropOn($anotherEl, uploadData);
  2786. expect(ev).toEqual(false);
  2787. });
  2788. it('drop on an element inside the table triggers upload', function() {
  2789. var ev;
  2790. ev = dropOn(fileList.$fileList.find('th:first'), uploadData);
  2791. expect(ev).not.toEqual(false);
  2792. expect(uploadData.targetDir).toEqual('/subdir');
  2793. });
  2794. it('drop on an element on the table container triggers upload', function() {
  2795. var ev;
  2796. ev = dropOn($('#app-content-files'), uploadData);
  2797. expect(ev).not.toEqual(false);
  2798. expect(uploadData.targetDir).toEqual('/subdir');
  2799. });
  2800. it('drop on an element inside the table does not trigger upload if no upload permission', function() {
  2801. $('#permissions').val(0);
  2802. var ev;
  2803. ev = dropOn(fileList.$fileList.find('th:first'), uploadData);
  2804. expect(ev).toEqual(false);
  2805. expect(notificationStub.calledOnce).toEqual(true);
  2806. });
  2807. it('drop on an folder does not trigger upload if no upload permission on that folder', function() {
  2808. var $tr = fileList.findFileEl('somedir');
  2809. var ev;
  2810. $tr.data('permissions', OC.PERMISSION_READ);
  2811. ev = dropOn($tr, uploadData);
  2812. expect(ev).toEqual(false);
  2813. expect(notificationStub.calledOnce).toEqual(true);
  2814. });
  2815. it('drop on a file row inside the table triggers upload to current folder', function() {
  2816. var ev;
  2817. ev = dropOn(fileList.findFileEl('One.txt').find('td:first'), uploadData);
  2818. expect(ev).not.toEqual(false);
  2819. expect(uploadData.targetDir).toEqual('/subdir');
  2820. });
  2821. it('drop on a folder row inside the table triggers upload to target folder', function() {
  2822. var ev;
  2823. ev = dropOn(fileList.findFileEl('somedir').find('td:eq(2)'), uploadData);
  2824. expect(ev).not.toEqual(false);
  2825. expect(uploadData.targetDir).toEqual('/subdir/somedir');
  2826. });
  2827. it('drop on a breadcrumb inside the table triggers upload to target folder', function() {
  2828. var ev;
  2829. fileList.changeDirectory('a/b/c/d');
  2830. ev = dropOn(fileList.$el.find('.crumb:eq(3)'), uploadData);
  2831. expect(ev).not.toEqual(false);
  2832. expect(uploadData.targetDir).toEqual('/a/b');
  2833. });
  2834. it('renders upload indicator element for folders only', function() {
  2835. fileList.add({
  2836. name: 'afolder',
  2837. type: 'dir',
  2838. mime: 'httpd/unix-directory'
  2839. });
  2840. fileList.add({
  2841. name: 'afile.txt',
  2842. type: 'file',
  2843. mime: 'text/plain'
  2844. });
  2845. expect(fileList.findFileEl('afolder').find('.uploadtext').length).toEqual(1);
  2846. expect(fileList.findFileEl('afile.txt').find('.uploadtext').length).toEqual(0);
  2847. });
  2848. });
  2849. describe('after folder creation due to folder upload', function() {
  2850. it('fetches folder info', function() {
  2851. var fetchInfoStub = sinon.stub(fileList, 'addAndFetchFileInfo');
  2852. uploader.trigger('createdfolder', '/subdir/newfolder');
  2853. expect(fetchInfoStub.calledOnce).toEqual(true);
  2854. expect(fetchInfoStub.getCall(0).args[0]).toEqual('newfolder');
  2855. expect(fetchInfoStub.getCall(0).args[1]).toEqual('/subdir');
  2856. fetchInfoStub.restore();
  2857. });
  2858. });
  2859. describe('after upload', function() {
  2860. var fetchInfoStub;
  2861. beforeEach(function() {
  2862. fetchInfoStub = sinon.stub(fileList, 'addAndFetchFileInfo');
  2863. });
  2864. afterEach(function() {
  2865. fetchInfoStub.restore();
  2866. });
  2867. function createUpload(name, dir) {
  2868. var jqXHR = {
  2869. status: 200
  2870. };
  2871. return {
  2872. getFileName: sinon.stub().returns(name),
  2873. getFullPath: sinon.stub().returns(dir),
  2874. data: {
  2875. jqXHR: jqXHR
  2876. }
  2877. };
  2878. }
  2879. /**
  2880. * Simulate add event on the given target
  2881. *
  2882. * @return event object including the result
  2883. */
  2884. function addFile(data) {
  2885. var ev = new $.Event('done', {
  2886. jqXHR: {status: 200}
  2887. });
  2888. var deferred = $.Deferred();
  2889. fetchInfoStub.returns(deferred.promise());
  2890. uploader.trigger('done', ev, data || {});
  2891. return deferred;
  2892. }
  2893. it('fetches file info', function() {
  2894. addFile(createUpload('upload.txt', '/subdir'));
  2895. expect(fetchInfoStub.calledOnce).toEqual(true);
  2896. expect(fetchInfoStub.getCall(0).args[0]).toEqual('upload.txt');
  2897. expect(fetchInfoStub.getCall(0).args[1]).toEqual('/subdir');
  2898. });
  2899. it('highlights all uploaded files after all fetches are done', function(done) {
  2900. var highlightStub = sinon.stub(fileList, 'highlightFiles');
  2901. var def1 = addFile(createUpload('upload.txt', '/subdir'));
  2902. var def2 = addFile(createUpload('upload2.txt', '/subdir'));
  2903. var def3 = addFile(createUpload('upload3.txt', '/another'));
  2904. uploader.trigger('stop', {});
  2905. expect(highlightStub.notCalled).toEqual(true);
  2906. def1.resolve();
  2907. expect(highlightStub.notCalled).toEqual(true);
  2908. def2.resolve();
  2909. def3.resolve();
  2910. setTimeout(function() {
  2911. expect(highlightStub.callCount).toEqual(1);
  2912. expect(highlightStub.getCall(0).args[0]).toEqual(['upload.txt', 'upload2.txt']);
  2913. highlightStub.restore();
  2914. done();
  2915. }, 5);
  2916. });
  2917. it('queries storage stats after all fetches are done', function(done) {
  2918. var statStub = sinon.stub(fileList, 'updateStorageStatistics');
  2919. var highlightStub = sinon.stub(fileList, 'highlightFiles');
  2920. var def1 = addFile(createUpload('upload.txt', '/subdir'));
  2921. var def2 = addFile(createUpload('upload2.txt', '/subdir'));
  2922. var def3 = addFile(createUpload('upload3.txt', '/another'));
  2923. uploader.trigger('stop', {});
  2924. expect(statStub.notCalled).toEqual(true);
  2925. def1.resolve();
  2926. expect(statStub.notCalled).toEqual(true);
  2927. def2.resolve();
  2928. def3.resolve();
  2929. setTimeout(function() {
  2930. expect(statStub.calledOnce).toEqual(true);
  2931. highlightStub.restore();
  2932. done();
  2933. }, 3);
  2934. });
  2935. });
  2936. });
  2937. describe('Handling errors', function () {
  2938. var deferredList;
  2939. var getFolderContentsStub;
  2940. var reloading;
  2941. beforeEach(function() {
  2942. deferredList = $.Deferred();
  2943. getFolderContentsStub =
  2944. sinon.stub(filesClient, 'getFolderContents');
  2945. getFolderContentsStub.onCall(0).returns(deferredList.promise());
  2946. getFolderContentsStub.onCall(1).returns($.Deferred().promise());
  2947. reloading = fileList.reload();
  2948. });
  2949. afterEach(function() {
  2950. getFolderContentsStub.restore();
  2951. fileList = undefined;
  2952. });
  2953. it('redirects to root folder in case of forbidden access', function (done) {
  2954. deferredList.reject(403);
  2955. return reloading.then(function() {
  2956. expect(fileList.getCurrentDirectory()).toEqual('/');
  2957. expect(getFolderContentsStub.calledTwice).toEqual(true);
  2958. }).then(done, done);
  2959. });
  2960. it('redirects to root folder and shows notification in case of internal server error', function (done) {
  2961. expect(notificationStub.notCalled).toEqual(true);
  2962. deferredList.reject(500);
  2963. return reloading.then(function() {
  2964. expect(fileList.getCurrentDirectory()).toEqual('/');
  2965. expect(getFolderContentsStub.calledTwice).toEqual(true);
  2966. expect(notificationStub.calledOnce).toEqual(true);
  2967. }).then(done, done);
  2968. });
  2969. it('redirects to root folder and shows notification in case of storage not available', function (done) {
  2970. expect(notificationStub.notCalled).toEqual(true);
  2971. deferredList.reject(503, 'Storage is temporarily not available');
  2972. return reloading.then(function() {
  2973. expect(fileList.getCurrentDirectory()).toEqual('/');
  2974. expect(getFolderContentsStub.calledTwice).toEqual(true);
  2975. expect(notificationStub.calledOnce).toEqual(true);
  2976. }).then(done, done);
  2977. });
  2978. });
  2979. describe('showFileBusyState', function() {
  2980. var $tr;
  2981. beforeEach(function() {
  2982. fileList.setFiles(testFiles);
  2983. $tr = fileList.findFileEl('Two.jpg');
  2984. });
  2985. it('shows spinner on busy rows', function() {
  2986. fileList.showFileBusyState('Two.jpg', true);
  2987. expect($tr.hasClass('busy')).toEqual(true);
  2988. expect($tr.find('.thumbnail').parent().attr('class'))
  2989. .toContain('icon-loading-small');
  2990. fileList.showFileBusyState('Two.jpg', false);
  2991. expect($tr.hasClass('busy')).toEqual(false);
  2992. expect(OC.TestUtil.getImageUrl($tr.find('.thumbnail')))
  2993. .toEqual(OC.imagePath('core', 'filetypes/image.svg'));
  2994. });
  2995. it('accepts multiple input formats', function() {
  2996. _.each([
  2997. 'Two.jpg',
  2998. ['Two.jpg'],
  2999. $tr,
  3000. [$tr]
  3001. ], function(testCase) {
  3002. fileList.showFileBusyState(testCase, true);
  3003. expect($tr.hasClass('busy')).toEqual(true);
  3004. fileList.showFileBusyState(testCase, false);
  3005. expect($tr.hasClass('busy')).toEqual(false);
  3006. });
  3007. });
  3008. });
  3009. describe('elementToFile', function() {
  3010. var $tr;
  3011. beforeEach(function() {
  3012. fileList.setFiles(testFiles);
  3013. $tr = fileList.findFileEl('One.txt');
  3014. });
  3015. it('converts data attributes to file info structure', function() {
  3016. var fileInfo = fileList.elementToFile($tr);
  3017. expect(fileInfo.id).toEqual(1);
  3018. expect(fileInfo.name).toEqual('One.txt');
  3019. expect(fileInfo.mtime).toEqual(123456789);
  3020. expect(fileInfo.etag).toEqual('abc');
  3021. expect(fileInfo.permissions).toEqual(OC.PERMISSION_ALL);
  3022. expect(fileInfo.size).toEqual(12);
  3023. expect(fileInfo.mimetype).toEqual('text/plain');
  3024. expect(fileInfo.type).toEqual('file');
  3025. expect(fileInfo.path).not.toBeDefined();
  3026. expect(fileInfo.isEncrypted).toEqual(false);
  3027. });
  3028. it('sets isEncrypted attribute if data includes true e2eencrypted', function() {
  3029. testFiles[3].isEncrypted = true;
  3030. fileList.setFiles(testFiles);
  3031. $tr = fileList.findFileEl('somedir');
  3032. var fileInfo = fileList.elementToFile($tr);
  3033. expect(fileInfo.isEncrypted).toEqual(true);
  3034. });
  3035. it('adds path attribute if available', function() {
  3036. $tr.attr('data-path', '/subdir');
  3037. var fileInfo = fileList.elementToFile($tr);
  3038. expect(fileInfo.path).toEqual('/subdir');
  3039. });
  3040. });
  3041. describe('new file menu', function() {
  3042. var newFileMenuStub;
  3043. beforeEach(function() {
  3044. newFileMenuStub = sinon.stub(OCA.Files.NewFileMenu.prototype, 'showAt');
  3045. });
  3046. afterEach(function() {
  3047. newFileMenuStub.restore();
  3048. })
  3049. it('renders new button when no legacy upload button exists', function() {
  3050. expect(fileList.$el.find('.button.upload').length).toEqual(0);
  3051. expect(fileList.$el.find('.button.new').length).toEqual(1);
  3052. });
  3053. it('does not render new button when no legacy upload button exists (public page)', function() {
  3054. fileList.destroy();
  3055. $('.files-controls').append('<input type="button" class="button upload" />');
  3056. fileList = new OCA.Files.FileList($('#app-content-files'));
  3057. expect(fileList.$el.find('.button.upload').length).toEqual(1);
  3058. expect(fileList.$el.find('.button.new').length).toEqual(0);
  3059. });
  3060. it('opens the new file menu when clicking on the "New" button', function() {
  3061. var $button = fileList.$el.find('.button.new');
  3062. $button.click();
  3063. expect(newFileMenuStub.calledOnce).toEqual(true);
  3064. });
  3065. it('does not open the new file menu when button is disabled', function() {
  3066. var $button = fileList.$el.find('.button.new');
  3067. $button.addClass('disabled');
  3068. $button.click();
  3069. expect(newFileMenuStub.notCalled).toEqual(true);
  3070. });
  3071. });
  3072. describe('mount type detection', function() {
  3073. function testMountType(dirInfoId, dirInfoMountType, inputMountType, expectedMountType) {
  3074. var $tr;
  3075. fileList.dirInfo.id = dirInfoId;
  3076. fileList.dirInfo.mountType = dirInfoMountType;
  3077. $tr = fileList.add({
  3078. type: 'dir',
  3079. mimetype: 'httpd/unix-directory',
  3080. name: 'test dir',
  3081. mountType: inputMountType
  3082. });
  3083. expect($tr.attr('data-mounttype')).toEqual(expectedMountType);
  3084. }
  3085. it('leaves mount type as is if no parent exists', function() {
  3086. testMountType(null, null, 'external', 'external');
  3087. testMountType(null, null, 'shared', 'shared');
  3088. });
  3089. it('detects share root if parent exists', function() {
  3090. testMountType(123, null, 'shared', 'shared-root');
  3091. testMountType(123, 'shared', 'shared', 'shared');
  3092. testMountType(123, 'shared-root', 'shared', 'shared');
  3093. });
  3094. it('detects external storage root if parent exists', function() {
  3095. testMountType(123, null, 'external', 'external-root');
  3096. testMountType(123, 'external', 'external', 'external');
  3097. testMountType(123, 'external-root', 'external', 'external');
  3098. });
  3099. });
  3100. describe('file list should not refresh if url does not change', function() {
  3101. var fileListStub;
  3102. beforeEach(function() {
  3103. fileListStub = sinon.stub(OCA.Files.FileList.prototype, 'changeDirectory');
  3104. fileList._currentDirectory = '/subdir';
  3105. });
  3106. afterEach(function() {
  3107. fileListStub.restore();
  3108. });
  3109. it('File list must not be refreshed', function() {
  3110. $('#app-content-files').trigger(new $.Event('urlChanged', {dir: '/subdir'}));
  3111. expect(fileListStub.notCalled).toEqual(true);
  3112. });
  3113. it('File list must be refreshed', function() {
  3114. $('#app-content-files').trigger(new $.Event('urlChanged', {dir: '/'}));
  3115. expect(fileListStub.notCalled).toEqual(false);
  3116. });
  3117. });
  3118. });