filelistSpec.js 126 KB

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